package com.zn.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.zn.entity.*;
import com.zn.feignClient.HomePageCallbackClient;
import com.zn.mapper.*;
import com.zn.service.OptionTradeService;
import com.zn.socket.SocketHandler;
import com.zn.task.MockTradeTask;
import com.zn.util.*;
import com.zn.vo.*;
import com.zn.web.dto.OptionTradeDto;
import com.zn.web.rpc.TradeRemoteCall;
import com.zn.web.vo.OpenOrderVo;
import com.zn.web.vo.OptionBuyAndSellVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author YinJiaWei
 * @version 2018/10/29 13:31
 */
@Service
public class OptionTradeServiceImpl extends BaseServiceImpl implements OptionTradeService {
    private static final Logger LOGGER = LoggerFactory.getLogger(OptionTradeServiceImpl.class);
    private static Double PRE_GROWTH_VALUE=0.005;

    @Autowired
    private OptionTradingGoodsMapper optionTradingGoodsMapper;
    @Autowired
    private AppCustomerMapper appCustomerMapper;
    @Autowired
    private OptionTradingContractMapper optionTradingContractMapper;
    @Autowired
    private OptionAccountMapper optionAccountMapper;
    @Autowired
    private TradeRemoteCall tradeRemoteCall;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private FutureReportOrderMapper futureReportOrderMapper;
    @Autowired
    private FutureOpenOrderMapper futureOpenOrderMapper;
    @Autowired
    private FutureCloseOrderMapper futureCloseOrderMapper;
    @Autowired
    private AgencyBusinessMapper agencyBusinessMapper;
    @Autowired
    private CSocketInfo socketInfo;
    @Autowired
    private MockTradeTask mockTradeTask;
    @Autowired
    private HedgingReportOrderMapper hedgingReportOrderMapper;
    @Autowired
    private HedgingOpenOrderMapper hedgingOpenOrderMapper;
    @Autowired
    private DocumentaryTradeServiceImpl documentaryTradeService;
    @Autowired
    private HomePageCallbackClient homePageCallbackClient;

    @Override
    public ResponseEntity<Message> checkBuyAndSell(OptionTradeDto optionTradeDto) {
        AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(optionTradeDto.getCustomerId());
        if(appCustomer==null){
            /**客户不存在**/
            return ResponseEntity.ok(Message.CUSTOMER_NOT_EXIST);
        }
        /**判断是否禁用 0-未禁用 1-禁用**/
        if(appCustomer.getDisableStatus()==1){
            //被禁用
            return ResponseEntity.ok(Message.CUSTOMER_IS_DISABLE);
        }
        /**判断认证状态 0-未认证 1-已认证 2-认证失败**/
        if(appCustomer.getAuthenticationStatus()!=1){
            //未认证
            return ResponseEntity.ok(Message.CUSTOMER_IS_NOT_AUTHENTICATION);
        }
        /**认证签名状态**/
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("customerId", appCustomer.getCustomerId());
        ResponseEntity<Message> bestSignResponse=homePageCallbackClient.getAppCustomerBestSignStatus(jsonObject);
        LOGGER.info("bestSignResponse---------->value:"+bestSignResponse.getBody().getResCode()+"|"+bestSignResponse.getBody().getMsg());
        if(!Message.BESTSIGN_CUSTOMER_NOT_OPENED.getResCode().equals(bestSignResponse.getBody().getResCode()) &&
                !Message.BESTSIGN_CUSTOMER_SUCCESS.getResCode().equals(bestSignResponse.getBody().getResCode())){
            return  bestSignResponse;
        }
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionTradeDto.getTargetCode());
        if(optionTradingGoods==null){
            /**当前标的不存在**/
            return ResponseEntity.ok(Message.TARGET_NOT_EXIST);
        }
        /**是否在交易时间内**/
        boolean checkTrading=this.checkTradingTime(optionTradingGoods.getTradingTime());
        if(!checkTrading){
            //当前非交易时间
            return ResponseEntity.ok(Message.NOT_TRADING_TIME);
        }
        OptionTradingContract optionTradingContract=new OptionTradingContract();
        optionTradingContract.setTradingGoodsId(optionTradingGoods.getTradingGoodsId());
        optionTradingContract.setContractMonthName(optionTradeDto.getContractMonthName());
        optionTradingContract.setIsTrading(0);
        List<OptionTradingContract> optionTradingContracts=optionTradingContractMapper.selectByWhere(optionTradingContract);
        if(optionTradingContracts==null || optionTradingContracts.size()==0){
            /**系统合约参数异常**/
            return ResponseEntity.ok(Message.CONTRACT_ARGS_ERROR);
        }
        OptionTradingContract optionTradingContractVo=optionTradingContracts.get(0);
        String today=DateUtils.convertDateToString(new Date());
        Object symbolObj=redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + today, optionTradeDto.getContraceCode());
        OptionSymbol optionSymbol=JSONObject.parseObject(symbolObj.toString(), OptionSymbol.class);
        if(optionSymbol==null){
            /**合约不存在**/
            return ResponseEntity.ok(Message.CONTRACT_NOT_FOUND);
        }
        /**是否进入风控时间**/
        boolean riskManagementFlag=this.checkInRiskManagement(optionSymbol.getRemainderDays(), optionTradingContractVo.getForceCloseAdvanceDay());
        if(!riskManagementFlag){
            return ResponseEntity.ok(Message.CONTRACE_ENTER_RISK_MANAGEMENT);
        }
        OptionBuyAndSellVo optionBuyAndSellVo=new OptionBuyAndSellVo(
                optionTradingGoods.getMinWavePoint(),optionTradingGoods.getTradingContractMultiplier(),
                optionTradingGoods.getTradingContractUnit(),optionTradingContractVo.getOpenFee(),
                optionTradingContractVo.getCloseFee(),optionTradingGoods.getReportMaxNum(),
                optionTradingGoods.getQuickOrderVolumes(),optionTradingGoods.getLimitReportMaxNum(),
                optionTradingGoods.getLimitQuickOrderVolumes()
        );
        return ResponseEntity.ok(Message.successData(optionBuyAndSellVo));
    }

    /**
     * 开仓报单
     * @param optionTradeDto
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public ResponseEntity<Message> reqsOpenReportOrder(OptionTradeDto optionTradeDto) throws Exception{
        AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(optionTradeDto.getCustomerId());
        /**获取合约信息**/
        String nowDate=DateUtils.convertDateToString(new Date());
        Object optionSymbolObj=getRedisSymbol(nowDate, optionTradeDto.getContraceCode(), 7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, optionTradeDto.getContraceCode());
        OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
        LOGGER.warn("optionSymbol info....."+optionSymbol.toString());
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionSymbol.getTargetCode());
        if(optionTradingGoods==null){
            /**当前标的不存在**/
            return ResponseEntity.ok(Message.TARGET_NOT_EXIST);
        }

        /**是否在交易时间内**/
        boolean checkTrading=this.checkTradingTime(optionTradingGoods.getTradingTime());
        if(!checkTrading){
            //当前非交易时间
            return ResponseEntity.ok(Message.NOT_TRADING_TIME);
        }
        OptionTradingContract optionTradingContract=new OptionTradingContract();
        optionTradingContract.setTradingGoodsId(optionTradingGoods.getTradingGoodsId());
        optionTradingContract.setContractMonthName(optionSymbol.getInstrumentMonth());
        optionTradingContract.setIsTrading(0);
        List<OptionTradingContract> optionTradingContracts=optionTradingContractMapper.selectByWhere(optionTradingContract);
        if(optionTradingContracts==null || optionTradingContracts.size()==0){
            /**系统合约参数异常**/
            return ResponseEntity.ok(Message.CONTRACT_ARGS_ERROR);
        }
        OptionTradingContract optionTradingContractVo=optionTradingContracts.get(0);
        /**是否进入风控时间**/
        boolean riskManagementFlag=this.checkInRiskManagement(optionSymbol.getRemainderDays(), optionTradingContractVo.getForceCloseAdvanceDay());
        if(!riskManagementFlag){
            return ResponseEntity.ok(Message.CONTRACE_ENTER_RISK_MANAGEMENT);
        }
        /**计算该笔订单需要的费用**/
        /**(卖一价+0.01)*10000*委托数量+开仓手续费*委托数量+平仓手续费*委托数量**/
        BigDecimal openPrice=new BigDecimal(optionTradeDto.getLimitPrice()).setScale(4, BigDecimal.ROUND_HALF_UP);
        if(optionTradeDto.getOrderPriceType()==0){
            /**如果市价报单 则加上多冻结的0.0020**/
            openPrice=openPrice.add(new BigDecimal(PRE_GROWTH_VALUE)).setScale(4, BigDecimal.ROUND_HALF_UP);
        }
        /**开仓总保证金 委托接*合约乘数*手数**/
        BigDecimal openDeposit=openPrice.multiply(optionTradingGoods.getTradingContractMultiplier()).multiply(new BigDecimal(optionTradeDto.getVolume())).setScale(4, BigDecimal.ROUND_HALF_UP);
        /**开仓总手续费**/
        BigDecimal openSumFee=optionTradingContractVo.getOpenFee().multiply(new BigDecimal(optionTradeDto.getVolume())).setScale(4, BigDecimal.ROUND_HALF_UP);
        /**平仓总手续费**/
        BigDecimal closeSumFee=optionTradingContractVo.getCloseFee().multiply(new BigDecimal(optionTradeDto.getVolume())).setScale(4, BigDecimal.ROUND_HALF_UP);
        /**校验客户资金**/
        /**计算可用资金=当前资金-当前冻结资金**/
        Double availableFunds=(appCustomer.getCustomerCapital()==null?0:appCustomer.getCustomerCapital())-(appCustomer.getCustomerFreezeCapital()==null?0:appCustomer.getCustomerFreezeCapital());
        Double sumTradeAmount=openDeposit.add(openSumFee).add(closeSumFee).doubleValue();
        if(sumTradeAmount>availableFunds){
            return ResponseEntity.ok(Message.LACK_OF_FUNDS);
        }
        /**开仓总成本手续费**/
        BigDecimal sumCostOpenFee=optionTradingContractVo.getCostOpenFee().multiply(new BigDecimal(optionTradeDto.getVolume())).setScale(4, BigDecimal.ROUND_HALF_UP);
        /**平仓总成本手续费**/
        BigDecimal sumCostCloseFee=optionTradingContractVo.getCostCloseFee().multiply(new BigDecimal(optionTradeDto.getVolume())).setScale(4, BigDecimal.ROUND_HALF_UP);
        /**开平仓总成本手续费**/
        Double sumCostTradeAmount=openDeposit.add(sumCostOpenFee).add(sumCostCloseFee).doubleValue();
        /**调接口校验机构方余额是否充足Begin**/
        Map<String,Object> businessCapitalResult=this.checkBusinessCapital(sumCostTradeAmount);
        boolean businessCapitalFlag=(boolean)businessCapitalResult.get("flag");
        if(!businessCapitalFlag){
            return ResponseEntity.ok(Message.BUSINESS_LACK_OF_FUNDS);
        }
        /**调接口校验机构方余额是否充足End**/
        /**查询启用的交易账户**/
        OptionAccount optionAccount=optionAccountMapper.selectAccountByStatus(0,1);
        /**交易账户信息**/
        if(optionAccount==null){
            /**交易账户异常**/
            return ResponseEntity.ok(Message.TRADE_ACCOUNT_ERROR);
        }
        /**获取orderRef**/
        String orderRef=getOrderRefStr();
        /**更新机构户中的资金Begin**/
        String version=businessCapitalResult.get("version").toString();
        String businessInfoId=businessCapitalResult.get("businessInfoId").toString();
        Response response=tradeRemoteCall.tradeCapitalChange(businessInfoId, orderRef, "0", version, optionTradeDto.getVolume().toString(), 0.00, sumCostTradeAmount);
        if(!"200".equals(response.getResCode())){
            /**更新机构户资金失败**/
            return ResponseEntity.ok(Message.UPDATE_BUSINESS_CAPITAL_FAIL);
        }
        /**更新机构户中的资金End**/
        /**插入成功之后开始冻结用户保证金Begin**/
        appCustomer.setCustomerFreezeCapital(sumTradeAmount);
        appCustomer.setCustomerCapital(null);
        LOGGER.info(appCustomer.getCustomerTradeAccount() + "refreshCapital reqsOpenReportOrder:操作冻结" + appCustomer.getCustomerFreezeCapital() + "|操作资金" + appCustomer.getCustomerCapital());
        int isSuccess=appCustomerMapper.refreshCapital(appCustomer);
        if(isSuccess==0){
            /**可用资金不足**/
            return ResponseEntity.ok(Message.LACK_OF_FUNDS);
        }
        /**插入成功之后开始冻结用户保证金End**/
        //拼接委托对象数据
        FutureReportOrder futureReportOrder=new FutureReportOrder();
        futureReportOrder.setReportId(UUIDUtils.getUuid());
        futureReportOrder.setCustomerId(appCustomer.getCustomerId());
        futureReportOrder.setInvestorId(optionAccount.getOptionTradeAccount());
        futureReportOrder.setInstrumentCode(optionTradeDto.getContraceCode());
        futureReportOrder.setInstrumentName(optionSymbol.getInstrumentName());
        futureReportOrder.setProductCode(optionSymbol.getTargetCode());
        futureReportOrder.setProductName(optionTradingGoods.getTradingGoodsName());
        futureReportOrder.setOrderRef(orderRef);
        futureReportOrder.setLimitPrice(openPrice);
        futureReportOrder.setDirection(optionTradeDto.getDirection());
        futureReportOrder.setOffsetFlag(optionTradeDto.getOffsetFlag());
        futureReportOrder.setVolume(optionTradeDto.getVolume());
        futureReportOrder.setCreateTime(new Date());
        futureReportOrder.setUpdateTime(new Date());
        /**补充报单价格类型**/
        futureReportOrder.setOrderPriceType(optionTradeDto.getOrderPriceType());
        //futureReportOrder.setStopLossPoint(new BigDecimal(futuresTradeDto.getStopLossPoint()).setScale(3, BigDecimal.ROUND_HALF_UP));
        //下单止损金额
        //futureReportOrder.setStopLossAmount(new BigDecimal(stopLossAmount).setScale(3, BigDecimal.ROUND_HALF_UP));
        //下单总保证金
        futureReportOrder.setFrozenDeposit(openDeposit);
        //总开仓平仓手续费
        futureReportOrder.setTotalFee(openSumFee.add(closeSumFee));
        /**开仓手续费单价**/
        futureReportOrder.setUnivalentOpenFee(optionTradingContractVo.getOpenFee());
        /**平仓手续费单价**/
        futureReportOrder.setUnivalentCloseFee(optionTradingContractVo.getCloseFee());
        /**开仓成本手续费单价**/
        futureReportOrder.setUnivalentCostOpenFee(optionTradingContractVo.getCostOpenFee());
        /**平仓成本手续费单价**/
        futureReportOrder.setUnivalentCostCloseFee(optionTradingContractVo.getCostCloseFee());
        futureReportOrder.setCostTotalFee(sumCostOpenFee.add(sumCostCloseFee));
        /**跟单标识**/
        futureReportOrder.setDocumentaryFlag(appCustomer.getDocumentaryStatus());
        futureReportOrderMapper.insertSelective(futureReportOrder);

        //拼接tcp/ip传输数据串 客户交易账号|母账户|合约code|本地报单编号|价格|手数|多空方向|开平方向|止损点位|订单报单类型（0-用户普通报单，1-对冲单，2-跟单）|报单价格类型（0-市价 1-限价）
        //reqorder|dev10001001|5321101611|10001588|dev190226100004|0.0055|2|0|0|0|1|1
        String dataStr="reqorder|"+appCustomer.getCustomerTradeAccount()+"|"+optionAccount.getOptionTradeAccount()+"|"+futureReportOrder.getInstrumentCode()+
                "|"+futureReportOrder.getOrderRef()+"|"+futureReportOrder.getLimitPrice()+"|"+futureReportOrder.getVolume()+"|"+futureReportOrder.getDirection()+"|"
                +futureReportOrder.getOffsetFlag()+"|0|"+Constant.ORDINARY_ORDER_FLAG+"|"+optionTradeDto.getOrderPriceType();
        SocketHandler socketHandler= null;
        LOGGER.info("reqsOpenReportOrder----->dataStr------------------->"+dataStr);
        try {
            /**推送资金记录**/
            tradeRemoteCall.sendCashFlowToCrm(futureReportOrder.getReportId(),1,0.00,sumTradeAmount);
            /**记录次条报单是由那台机器发出的**/
            redisTemplate.opsForValue().set(Constant.ORDER_REF+futureReportOrder.getOrderRef(),LocalInetAddress.getLocalAddress(),12,TimeUnit.HOURS);
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            boolean flag=socketHandler.isServerClose();
            if(flag){
                throw new Exception("socket连接断开异常");
            }
            LOGGER.info("dataStr:" + dataStr);
            redisTemplate.opsForValue().set(Constant.IDEMPOTENCY_REQSOPENREPORTORDER + optionTradeDto.getCustomerId() + ":" + optionTradeDto.getContraceCode(), optionTradeDto.getIdempotency(), 10, TimeUnit.MINUTES);
            socketHandler.sendMessage(dataStr);
//            mockTradeTask.mockReportOrder(this, dataStr);
            if(appCustomer.getDocumentaryStatus() !=null && appCustomer.getDocumentaryStatus()!=0){
                /**发起跟单操作**/
                documentaryTradeService.reqsOpenDocumentaryReportOrder(futureReportOrder);
            }
            return ResponseEntity.ok(Message.REQ_SUCCESS);
        } catch (Exception e) {
            LOGGER.error("connection socket err .....", e);
            //手动回滚数据
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            redisTemplate.delete(Constant.IDEMPOTENCY_REQSOPENREPORTORDER + optionTradeDto.getCustomerId() + ":" + optionTradeDto.getContraceCode());
            /**撤回机构冻结资金Begin**/
            tradeRemoteCall.tradeCapitalChange(businessInfoId, futureReportOrder.getOrderRef(), "1", version, futureReportOrder.getVolume().toString(), 0.00, sumCostTradeAmount);
            /**撤回机构冻结资金End**/
            /**推送资金记录撤回**/
            tradeRemoteCall.sendCashFlowToCrm(futureReportOrder.getReportId(),6,0.00,sumTradeAmount);
            return ResponseEntity.ok(Message.SYSTEM_CONNECT_ERROR);
        }
    }
    /**
     * 平仓报单
     * @param optionTradeDto
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public ResponseEntity<Message> reqsCloseReportOrder(OptionTradeDto optionTradeDto) throws Exception{
        AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(optionTradeDto.getCustomerId());
        /**获取合约信息**/
        String nowDate=DateUtils.convertDateToString(new Date());
        Object optionSymbolObj=getRedisSymbol(nowDate,optionTradeDto.getContraceCode(),7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, optionTradeDto.getContraceCode());
        OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
        LOGGER.warn("optionSymbol info....."+optionSymbol.toString());
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionSymbol.getTargetCode());
        if(optionTradingGoods==null){
            /**当前标的不存在**/
            return ResponseEntity.ok(Message.TARGET_NOT_EXIST);
        }

        /**是否在交易时间内**/
        boolean checkTrading=this.checkTradingTime(optionTradingGoods.getTradingTime());
        if(!checkTrading){
            //当前非交易时间
            return ResponseEntity.ok(Message.NOT_TRADING_TIME);
        }
        //校验该用户是否有平仓手数
        List<FutureOpenOrder> futureOpenOrderList=futureOpenOrderMapper.selectListByOrderRef(optionTradeDto.getOpenOrderRef());
        /**判断认证状态 0-未认证 1-已认证 2-认证失败**/
        if(appCustomer.getAuthenticationStatus()!=1){
            //未认证
            return ResponseEntity.ok(Message.CUSTOMER_IS_NOT_AUTHENTICATION);
        }
        if(futureOpenOrderList==null){
            return ResponseEntity.ok(Message.ARGS_ERROR);
        }
        Integer effectiveHoldVolume=0;
        for(FutureOpenOrder obj:futureOpenOrderList){
            //平仓用户与开仓用户不匹配
            if(!obj.getCustomerId().equals(appCustomer.getCustomerId())){
                return ResponseEntity.ok(Message.CUSTOMER_MATCHING_ERROR);
            }
            //平仓的合约于开仓的合约code不一致
            if(!obj.getInstrumentCode().equals(optionTradeDto.getContraceCode())){
                return ResponseEntity.ok(Message.CONTRACECODE_MATCHING_ERROR);
            }
            effectiveHoldVolume=effectiveHoldVolume+obj.getEffectiveHoldVolume();
        }
        //可用手数<平仓手数 提示平仓手数不足
        if(effectiveHoldVolume<optionTradeDto.getVolume()){
            return ResponseEntity.ok(Message.HOLD_VOLUME_INSUFFICIENT);
        }
        /**由那个账户开仓的 由那个账户平仓**/
        FutureOpenOrder futureOpenOrder=futureOpenOrderList.get(0);
        OptionAccount optionAccount=optionAccountMapper.selectAccountByStatus(0,1);
        /**交易账户信息**/
        if(optionAccount==null){
            /**交易账户异常**/
            return ResponseEntity.ok(Message.TRADE_ACCOUNT_ERROR);
        }
        //查询开仓报单时的开仓手续费和平仓手续费
        FutureReportOrder openReportOrder=futureReportOrderMapper.selectByOrderRef(optionTradeDto.getOpenOrderRef(), optionTradeDto.getContraceCode());
        //拼接委托对象数据
        FutureReportOrder futureReportOrder=new FutureReportOrder();
        futureReportOrder.setReportId(UUIDUtils.getUuid());
        futureReportOrder.setCustomerId(optionTradeDto.getCustomerId());
        futureReportOrder.setInvestorId(futureOpenOrder.getInvestorId());
        futureReportOrder.setProductCode(optionSymbol.getTargetCode());
        futureReportOrder.setProductName(optionTradingGoods.getTradingGoodsName());
        futureReportOrder.setInstrumentCode(optionTradeDto.getContraceCode());
        futureReportOrder.setInstrumentName(optionSymbol.getInstrumentName());
        futureReportOrder.setOrderRef(getOrderRefStr());
        futureReportOrder.setUnivalentOpenFee(openReportOrder.getUnivalentOpenFee());
        futureReportOrder.setUnivalentCloseFee(openReportOrder.getUnivalentCloseFee());
        futureReportOrder.setUnivalentCostOpenFee(openReportOrder.getUnivalentCostOpenFee());
        futureReportOrder.setUnivalentCostCloseFee(openReportOrder.getUnivalentCostCloseFee());
        BigDecimal closePrice=new BigDecimal(optionTradeDto.getLimitPrice()).setScale(4,BigDecimal.ROUND_HALF_UP);
        //最低价
//        BigDecimal minPrice=new BigDecimal(0.0001).setScale(4,BigDecimal.ROUND_HALF_UP);
//        int flagVal=closePrice.compareTo(minPrice);
        //如果平仓价小于最低价 则使用最低价
//        if(flagVal==-1){
//            closePrice=minPrice;
//        }
        futureReportOrder.setLimitPrice(closePrice);
        futureReportOrder.setDirection(optionTradeDto.getDirection().toString());
        futureReportOrder.setOffsetFlag(optionTradeDto.getOffsetFlag().toString());
        futureReportOrder.setVolume(optionTradeDto.getVolume());
        futureReportOrder.setCreateTime(new Date());
        futureReportOrder.setUpdateTime(new Date());
        /**补充报单价格类型**/
        futureReportOrder.setOrderPriceType(optionTradeDto.getOrderPriceType());
        //平仓报单录入数据
        futureReportOrderMapper.insertSelective(futureReportOrder);
        //递归去扣可用手数
        this.buckleEffectiveHoldVolume(optionTradeDto.getOpenOrderRef(), optionTradeDto.getVolume());
        //修改成功 保存平仓时的部分数据
        FutureCloseOrder futureCloseOrder=new FutureCloseOrder();
        futureCloseOrder.setCloseId(UUIDUtils.getUuid());
        futureCloseOrder.setOpenOrderRef(optionTradeDto.getOpenOrderRef());
        futureCloseOrder.setCustomerId(futureReportOrder.getCustomerId());
        futureCloseOrder.setInvestorId(futureOpenOrder.getInvestorId());
        futureCloseOrder.setProductCode(optionSymbol.getTargetCode());
        futureCloseOrder.setProductName(optionTradingGoods.getTradingGoodsName());
        futureCloseOrder.setInstrumentCode(optionTradeDto.getContraceCode());
        futureCloseOrder.setInstrumentName(optionSymbol.getInstrumentName());
        futureCloseOrder.setReportId(futureReportOrder.getReportId());
        /**暂时不给开仓价**/
//        futureCloseOrder.setOpenPrice(futureOpenOrder.getPrice());
        futureCloseOrder.setStatus(0);
        futureCloseOrder.setCreateTime(new Date());
        futureCloseOrder.setUpdateTime(new Date());
        futureCloseOrderMapper.insertSelective(futureCloseOrder);
        //拼接tcp/ip传输数据串 reqorder|客户交易账号|母账户|合约code|本地报单编号|价格|手数|多空方向|开平方向|开仓本地报单编号|订单报单类型（0-用户普通报单，1-对冲单，2-跟单）|报单价格类型（0-市价 1-限价）
        String dataStr="reqorder|"+appCustomer.getCustomerTradeAccount()+"|" +futureOpenOrder.getInvestorId() +"|"+futureReportOrder.getInstrumentCode()+
                "|"+futureReportOrder.getOrderRef()+"|"+futureReportOrder.getLimitPrice()+"|"+futureReportOrder.getVolume()+
                "|"+futureReportOrder.getDirection()+"|"+futureReportOrder.getOffsetFlag()+"|"+optionTradeDto.getOpenOrderRef()+"|"+Constant.ORDINARY_ORDER_FLAG+"|"+optionTradeDto.getOrderPriceType();
        SocketHandler socketHandler= null;
        LOGGER.info("reqsCloseReportOrder-->dataStr------------------->"+dataStr);
        try {
            /**记录次条报单是由那台机器发出的 60s后自动删除**/
            redisTemplate.opsForValue().set(Constant.ORDER_REF+futureReportOrder.getOrderRef(),LocalInetAddress.getLocalAddress(),12,TimeUnit.HOURS);
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            /**判断是否断开连接，断开返回true,没有返回false**/
            boolean flag=socketHandler.isServerClose();
            if(flag){
                throw new Exception("socket连接断开异常");
            }
            LOGGER.info("close dataStr:" + dataStr);
            socketHandler.sendMessage(dataStr);
            if(StringUtils.isNotBlank(optionTradeDto.getIdempotency())){
                redisTemplate.opsForValue().set(Constant.IDEMPOTENCY_REQSCLOSEREPORTORDER + optionTradeDto.getCustomerId() + ":" + optionTradeDto.getContraceCode(), optionTradeDto.getIdempotency(), 10, TimeUnit.MINUTES);
            }
//            mockTradeTask.mockReportOrder(this,dataStr);
            closeIsDocumentary(futureReportOrder, optionTradeDto.getOpenOrderRef());
            return ResponseEntity.ok(Message.REQ_SUCCESS);
        } catch (Exception e) {
            LOGGER.error("reqsCloseReportOrder connection socket err ....." , e);
            //手动回滚数据
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            if(StringUtils.isNotBlank(optionTradeDto.getIdempotency())) {
                redisTemplate.delete(Constant.IDEMPOTENCY_REQSOPENREPORTORDER + optionTradeDto.getCustomerId() + ":" + optionTradeDto.getContraceCode());
            }
            return ResponseEntity.ok(Message.SYSTEM_CONNECT_ERROR);
        }
    }

    /**
     * 该平仓是否跟单平仓
     */
    private void closeIsDocumentary(FutureReportOrder futureReportOrder,String openOrderRef){
        try {
            /**开仓时的报单对象**/
            FutureReportOrder futureOpenReportOrder=futureReportOrderMapper.selectByOrderRef(openOrderRef, futureReportOrder.getInstrumentCode());
            if(futureOpenReportOrder != null && futureOpenReportOrder.getDocumentaryFlag()!=null && futureOpenReportOrder.getDocumentaryFlag()!=0){
                futureReportOrder.setDocumentaryFlag(futureOpenReportOrder.getDocumentaryFlag());
                futureReportOrderMapper.updateByPrimaryKeySelective(futureReportOrder);
                /**说明该用户平仓的单子有进行跟单操作**/
                LOGGER.info("closeIsDocumentary--->"+openOrderRef);
                documentaryTradeService.reqsCloseDocumentaryReportOrder(futureReportOrder,openOrderRef,futureOpenReportOrder.getDocumentaryFlag());
            }
        }catch (Exception e){
            LOGGER.error("closeIsDocumentary ---->error:",e);
        }
    }
    /**
     * 撤单请求
     * @param optionTradeDto
     * @return
     * @throws Exception
     */
    @Override
    public ResponseEntity<Message> reqsCancelReportOrder(OptionTradeDto optionTradeDto) throws Exception {
        AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(optionTradeDto.getCustomerId());
        Map<String,Object> map = new HashMap<>();
        map.put("orderRef", optionTradeDto.getOpenOrderRef());
        FutureReportOrder futureReportOrder=futureReportOrderMapper.selectByOrderRef(optionTradeDto.getOpenOrderRef(), optionTradeDto.getContraceCode());
        if(futureReportOrder==null || appCustomer==null){
            return ResponseEntity.ok(Message.ARGS_ERROR);
        }
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(futureReportOrder.getProductCode());
        /**是否在交易时间内**/
        boolean checkTrading = this.checkTradingTime(optionTradingGoods.getTradingTime());
        if(!checkTrading){
            //当前非交易时间
            return ResponseEntity.ok(Message.NOT_TRADING_TIME);
        }
        //拼接tcp/ip传输数据串 cancelOrder|母账户|子账户|外部请求委托|
        String dataStr="cancelOrder|"+futureReportOrder.getInvestorId()+"|"+appCustomer.getCustomerTradeAccount()+"|"+optionTradeDto.getOpenOrderRef();
        SocketHandler socketHandler= null;
        try {
            /**记录次条报单是由那台机器发出的**/
            //redisTemplate.opsForValue().set(Constant.ORDER_REF+futureReportOrder.getOrderRef(),LocalInetAddress.getLocalAddress(),12,TimeUnit.HOURS);
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            boolean flag=socketHandler.isServerClose();
            if(flag){
                throw new Exception("socket连接断开异常");
            }
            LOGGER.info("dataStr:" + dataStr);
            redisTemplate.opsForValue().set(Constant.IDEMPOTENCY_REQSOPENREPORTORDER + optionTradeDto.getCustomerId() + ":" + optionTradeDto.getContraceCode(), optionTradeDto.getIdempotency(), 10, TimeUnit.MINUTES);
            socketHandler.sendMessage(dataStr);
            if(futureReportOrder.getDocumentaryFlag()!=null && futureReportOrder.getDocumentaryFlag()!=0){
                /**说明该用户平仓的单子有进行跟单操作**/
                documentaryTradeService.reqsCancelDocumentaryReportOrder(futureReportOrder.getOrderRef());
            }
            return ResponseEntity.ok(Message.REQ_SUCCESS);
        } catch (Exception e) {
            LOGGER.error("connection socket err .....", e);
            //手动回滚数据
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            redisTemplate.delete(Constant.IDEMPOTENCY_REQSOPENREPORTORDER + optionTradeDto.getCustomerId()+":"+optionTradeDto.getContraceCode());
            return ResponseEntity.ok(Message.SYSTEM_CONNECT_ERROR);
        }
    }
    /**
     * 报单响应
     * @param respData
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public ResponseEntity<Message> respReportOrder(String respData) throws Exception {
        //交易日|报单提交状态|报单状态|报单编号|报单来源|报单类型|数量|最小成交量|今成交数量|报单价格|本地报单编号|合约|状态信息|报单时间|错误ID
        //交易日|报单提交状态|报单状态|报单编号|报单来源|报单类型|数量|最小成交量|今成交数量|报单价格|本地报单编号|合约|状态信息|报单时间|errid| (接口类型)
        //report|20190218|0|a|2008246|0|0|1|1|0|0.0035|a190218100016|10001703|报单已提交|14:51:52|0|0|0|
        //report|20190219|0|a||0|0|1|1|0|0.0024|a190219100004|10001668|报单已提交|10:16:11|0|4|0|0|
        LOGGER.info("respReportOrder:" + respData);
        String [] respReportData=respData.split("\\|");
        String tradingDay=respReportData[1],submitStatus=respReportData[2],orderStatus=respReportData[3],orderSysId=respReportData[4],orderSource=respReportData[5],
                orderType=respReportData[6],volume=respReportData[7],minVolume=respReportData[8],tradeVolume=respReportData[9],limitPrice=respReportData[10],
                orderRef=respReportData[11],instrumentCode=respReportData[12],statusMsg=respReportData[13],tradeTime=respReportData[14],errorCode=respReportData[15],
                tradeServerCode=respReportData[16];
        //匹配本地报单编号
        FutureReportOrder futureReportOrder=futureReportOrderMapper.selectByOrderRef(orderRef, instrumentCode);
        if(futureReportOrder==null){
            LOGGER.error("c++ 传过来的本地报单编号在库中未找到：" + respData);
            return ResponseEntity.ok(Message.REPORT_ORDER_NOT_EXIST);
        }
        if(StringUtils.isNotBlank(futureReportOrder.getOrderStatus()) && ("5".equals(orderStatus) || "0".equals(orderStatus)) &&
                (futureReportOrder.getOrderStatus().contains("0") || futureReportOrder.getOrderStatus().contains("5"))){
            //报单已存在结束标识
            return ResponseEntity.ok(Message.REPORT_ORDER_END);
        }
        //如果出现错误回报则
        if(!"0".equals(errorCode)){
            /**orderStatus设置为错单**/
            orderStatus="99";
            statusMsg=CTPErrorCode.codeConvertMsg(errorCode);
            /**判断是否是份额不足导致失败**/
            if("9999".equals(errorCode) || "8888".equals(errorCode) || "31".equals(errorCode)){
                /**自动切换交易报单母账户逻辑**/
                switchOptionAccount(errorCode,1);
            }
        }
        futureReportOrder.setUpdateTime(new Date());
        futureReportOrder.setTradingDay(tradingDay);
        if(StringUtils.isNotBlank(futureReportOrder.getOrderStatus())){
            String dbOrderStatus=futureReportOrder.getOrderStatus();
            //获取最后一位状态
            String lastStatus=dbOrderStatus.substring(dbOrderStatus.length()-1);
            /**如果出现位置回报顺序错乱的情况**/
            if("0".equals(lastStatus) || "5".equals(lastStatus)){
                /**订单状态重新拼接**/
                String subOrderStatus=dbOrderStatus.substring(0,dbOrderStatus.length()-1);
                String newOrderStatus=subOrderStatus+orderStatus+"|"+lastStatus;
                futureReportOrder.setOrderStatus(newOrderStatus);
            }else{
                futureReportOrder.setOrderStatus(dbOrderStatus+orderStatus+"|");
            }
        }else{
            futureReportOrder.setOrderStatus(orderStatus + "|");
        }
        if(StringUtils.isNotBlank(futureReportOrder.getSubmitStatus())){
            String dbOrderStatus=futureReportOrder.getOrderStatus();
            String dbSubmitStatus=futureReportOrder.getSubmitStatus();
            //获取最后一位状态
            String lastOrderStatus=dbOrderStatus.substring(dbOrderStatus.length()-1);
            String lastSubmitStatus=dbSubmitStatus.substring(dbSubmitStatus.length()-1);
            /**如果出现位置回报顺序错乱的情况**/
            if("0".equals(lastOrderStatus) || "5".equals(lastOrderStatus)){
                /**订单状态重新拼接**/
                String subSubmitStatus=dbSubmitStatus.substring(0,dbSubmitStatus.length()-1);
                String newSubmitStatus=subSubmitStatus+orderStatus+"|"+lastSubmitStatus;
                futureReportOrder.setSubmitStatus(newSubmitStatus);
            }else{
                futureReportOrder.setSubmitStatus(dbSubmitStatus + submitStatus + "|");
            }
        }else{
            futureReportOrder.setSubmitStatus(submitStatus+"|");
        }
        futureReportOrder.setOrderSysId(orderSysId);
        futureReportOrder.setOrderSource(orderSource);
        futureReportOrder.setOrderType(orderType);
        futureReportOrder.setMinVolume(Integer.valueOf(minVolume));
        if(futureReportOrder.getTradeVolume()<Integer.valueOf(tradeVolume)){
            futureReportOrder.setTradeVolume(Integer.valueOf(tradeVolume));
        }
        futureReportOrder.setLimitPrice(new BigDecimal(limitPrice));
        futureReportOrder.setVolume(Integer.valueOf(volume));
        futureReportOrder.setStatusMsg(statusMsg);
        futureReportOrder.setReportTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
        futureReportOrder.setRespCStatus(1); //得到c++的响应标识
        futureReportOrder.setTradeServerCode(tradeServerCode);
        futureReportOrderMapper.updateByPrimaryKeySelective(futureReportOrder);
        String businessInfoId=agencyBusinessMapper.selectByBusinessLevel().getUniqueIdentification();
        /**封装报单响应推送模型**/
        PushSocketModel pushSocketModel=new PushSocketModel();
        pushSocketModel.setInstrumentName(futureReportOrder.getInstrumentName());
        pushSocketModel.setInstrumentCode(futureReportOrder.getInstrumentCode());
        pushSocketModel.setLimitPrice(futureReportOrder.getLimitPrice());
        pushSocketModel.setTradeVolume(futureReportOrder.getTradeVolume());
        pushSocketModel.setStatusMsg(futureReportOrder.getStatusMsg());
        pushSocketModel.setOrderNo(futureReportOrder.getOrderRef());
        pushSocketModel.setVolume(futureReportOrder.getVolume());
        pushSocketModel.setOrderPriceType(futureReportOrder.getOrderPriceType());
        //如果是开仓
        if("0".equals(futureReportOrder.getOffsetFlag())){
            /**如果是开仓 且开仓异常撤回用户的冻结保证金**/
            if("99".equals(orderStatus)){
                /**撤回用户该笔委托冻结资金逻辑**/
                //需要撤回的冻结资金=总保证金+总手续费
                Double sumOrderFreezeCapital=futureReportOrder.getFrozenDeposit().add(futureReportOrder.getTotalFee()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                /****************更新用户资金操作******************/
                AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(futureReportOrder.getCustomerId());
                //更新保证金费用
                appCustomer.setCustomerFreezeCapital(-sumOrderFreezeCapital);
                appCustomer.setCustomerCapital(null);
                //修改用户冻结资金
//                LOGGER.info("respReportOrderOpen------->refreshCapital:" + appCustomer.toString());
                LOGGER.info(appCustomer.getCustomerTradeAccount() + "refreshCapital respReportOrdercuodan:操作冻结" + appCustomer.getCustomerFreezeCapital() + "|操作资金" + appCustomer.getCustomerCapital());
                appCustomerMapper.refreshCapital(appCustomer);
                /**撤单-异步推送socket**/
                sendNotifyChannel(futureReportOrder.getCustomerId(), Constant.TRADE_ORDER, pushSocketModel);
                /**机构户资金撤回Begin**/
                /**机构户需要撤回的冻结资金=总保证金+总成本手续费**/
                BigDecimal sumBusinessOrderFreezeCapital=futureReportOrder.getFrozenDeposit().add(futureReportOrder.getCostTotalFee());
                tradeRemoteCall.tradeCapitalChange(businessInfoId, orderRef, "2", "0", volume, 0.00,sumBusinessOrderFreezeCapital.doubleValue());
                /**机构户资金撤回End**/
                /**推送资金记录撤回**/
                tradeRemoteCall.sendCashFlowToCrm(futureReportOrder.getReportId(),6,0.00,sumOrderFreezeCapital);
            }
            /**如果接收到响应的撤单指令 或者收到订单状态为a且是qwin的程序 则将客户冻结资金撤回**/
            if("5".equals(orderStatus) || ("a".equals(orderStatus) && Constant.QWIN_OPTION_SERVER.equals(tradeServerCode))){
                /**获取撤单手数**/
                BigDecimal cancelVolume=new BigDecimal(futureReportOrder.getVolume()-futureReportOrder.getTradeVolume());
                /**委托单笔开仓费用 总冻结资金/委托手数**/
                BigDecimal reportSinglePrice=futureReportOrder.getFrozenDeposit().divide(new BigDecimal(futureReportOrder.getVolume()));
                /**撤回保证金**/
                BigDecimal cancelCapital=reportSinglePrice.multiply(cancelVolume);
                /**获取开仓时 开仓手续费/张 平仓手续费/张**/
//                BigDecimal reportVolume=new BigDecimal(futureReportOrder.getVolume());
                BigDecimal openFee=futureReportOrder.getUnivalentOpenFee();
                BigDecimal closeFee=futureReportOrder.getUnivalentCloseFee();
                /**需撤回的成本手续费(开仓手续费+平仓手续费)*撤单手数**/
                BigDecimal cancelPoundage=openFee.add(closeFee).multiply(cancelVolume);
                /****************更新用户资金操作******************/
                AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(futureReportOrder.getCustomerId());
                /**撤销冻结保证金费用**/
                Double cancelFreezeCapital=(cancelCapital.add(cancelPoundage)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                appCustomer.setCustomerFreezeCapital(-cancelFreezeCapital);
                appCustomer.setCustomerCapital(null);
//                LOGGER.info("respReportOrderChedan------>refreshCapital:" + appCustomer.toString());
                LOGGER.info(appCustomer.getCustomerTradeAccount() + "refreshCapital respReportOrderChedan:操作冻结" + appCustomer.getCustomerFreezeCapital() + "|操作资金" + appCustomer.getCustomerCapital());
                appCustomerMapper.refreshCapital(appCustomer);
                /**撤单-异步推送socket**/
                sendNotifyChannel(futureReportOrder.getCustomerId(), Constant.TRADE_ORDER, pushSocketModel);
                /**机构户资金撤回Begin!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**/
                /**撤销机构冻结保证金费用**/
                BigDecimal costOpenFee=futureReportOrder.getUnivalentCostOpenFee();
                BigDecimal costCloseFee=futureReportOrder.getUnivalentCostCloseFee();
                /**需撤回的成本手续费(开仓手续费+平仓手续费)*撤单手数**/
                BigDecimal cancelCostPoundage=costOpenFee.add(costCloseFee).multiply(cancelVolume);
                Double cancelCostFreezeCapital=(cancelCapital.add(cancelCostPoundage)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                tradeRemoteCall.tradeCapitalChange(businessInfoId, orderRef, "2", "", volume, 0.00, cancelCostFreezeCapital);
                /**机构户资金撤回End!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**/
                /**推送资金记录撤回**/
                tradeRemoteCall.sendCashFlowToCrm(futureReportOrder.getReportId(), 6, 0.00, cancelFreezeCapital);
            }
        }else{
            //如果是平仓 且订单状态为撤单,错单
            if("5".equals(orderStatus) || "99".equals(orderStatus) || ("a".equals(orderStatus) && Constant.QWIN_OPTION_SERVER.equals(tradeServerCode))){
                FutureCloseOrder futureCloseOrder=futureCloseOrderMapper.selectByReportId(futureReportOrder.getReportId());
                //获取撤单手数
                int withdrawNum=futureReportOrder.getVolume()-futureReportOrder.getTradeVolume();
                //递归将扣除的可用手数加回来
                this.replyEffectiveHoldVolume(futureCloseOrder.getOpenOrderRef(), withdrawNum);
                /**撤单-异步推送socket**/
                sendNotifyChannel(futureReportOrder.getCustomerId(), Constant.TRADE_ORDER, pushSocketModel);
            }
        }
        /**增加过程状态推送 3-报单已提交 1-部分成交**/
        if("3".equals(orderStatus) || "1".equals(orderStatus)){
            /**报单过程状态推送**/
            sendNotifyChannel(futureReportOrder.getCustomerId(), Constant.REPORT_PROCESS_ORDER, pushSocketModel);
        }
        return ResponseEntity.ok(Message.REQ_SUCCESS);
    }

    /**
     * 根据合约code 获取合约费率对象
     * @param instrumentCode
     * @return
     */
    public OptionTradingContract getInstrumentFree(String instrumentCode){
        String nowDate=DateUtils.convertDateToString(new Date());
        Object optionSymbolObj=getRedisSymbol(nowDate, instrumentCode, 7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO+nowDate,instrumentCode);
        OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionSymbol.getTargetCode());
        OptionTradingContract optionTradingContract=new OptionTradingContract();
        optionTradingContract.setTradingGoodsId(optionTradingGoods.getTradingGoodsId());
        optionTradingContract.setContractMonthName(optionSymbol.getInstrumentMonth());
        optionTradingContract.setIsTrading(0);
        List<OptionTradingContract> optionTradingContracts=optionTradingContractMapper.selectByWhere(optionTradingContract);
        OptionTradingContract optionTradingContractVo=optionTradingContracts.get(0);
        return optionTradingContractVo;
    }

    /**
     * 成交回报
     * @param respData
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public ResponseEntity<Message> respDealOrder(String respData) throws Exception {
        //开平方向|本地报单编号|合约code|报单编号|成交编号|成交日期|成交时间|手数|价格|买卖类型|交易类型
        //deal|4|180927100195|RM901|      204205|       80029|20180927|14:55:01|1|2484.000|1|0|
        LOGGER.info("respReportOrder:" + respData);
        String [] respDealData=respData.split("\\|");
        String offsetFlag=respDealData[1],orderRef=respDealData[2],instrumentCode=respDealData[3],orderSysId=respDealData[4],
                tradeId=respDealData[5],tradingDay=respDealData[6],tradeTime=respDealData[7],volume=respDealData[8],price=respDealData[9],
                direction=respDealData[10],tradeType=respDealData[11];
        //获取开平标识
        //匹配本地报单编号 orderRef,instrumentCode
        FutureReportOrder futureReportOrder=futureReportOrderMapper.selectByOrderRef(orderRef, instrumentCode);
        if(futureReportOrder==null){
            LOGGER.error("成交--传过来的本地报单编号在库中未找到：" + respData);
            return ResponseEntity.ok(Message.SYS_ERROR);
        }
        String nowDate=DateUtils.convertDateToString(new Date());
        Object optionSymbolObj=getRedisSymbol(nowDate, instrumentCode, 7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, instrumentCode);
        OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
        /**根据商品code查询商品的 最小波动点位等信息**/
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionSymbol.getTargetCode());
        String orderId="",orderType="";
        BigDecimal actualVolume=new BigDecimal(volume);
        PushSocketModel pushSocketModel=new PushSocketModel();
        String businessInfoId=agencyBusinessMapper.selectByBusinessLevel().getUniqueIdentification();
        /**获取开仓时的费率 开仓手续费/张 平仓手续费/张**/
        BigDecimal univalentOpenFee=futureReportOrder.getUnivalentOpenFee();
        BigDecimal univalentCloseFee=futureReportOrder.getUnivalentCloseFee();
        BigDecimal univalentCostOpenFee=futureReportOrder.getUnivalentCostOpenFee();
        BigDecimal univalentCostCloseFee=futureReportOrder.getUnivalentCostCloseFee();
        /**开仓回报**/
        if("0".equals(offsetFlag)){
            FutureOpenOrder futureOpenOrder=new FutureOpenOrder();
            futureOpenOrder.setOpenId(UUIDUtils.getUuid());
            futureOpenOrder.setCustomerId(futureReportOrder.getCustomerId());
            futureOpenOrder.setInvestorId(futureReportOrder.getInvestorId());
            futureOpenOrder.setProductCode(futureReportOrder.getProductCode());
            futureOpenOrder.setProductName(futureReportOrder.getProductName());
            futureOpenOrder.setInstrumentCode(futureReportOrder.getInstrumentCode());
            futureOpenOrder.setInstrumentName(futureReportOrder.getInstrumentName());
            futureOpenOrder.setReportId(futureReportOrder.getReportId());
            futureOpenOrder.setOrderSysId(orderSysId);
            futureOpenOrder.setTradeId(tradeId);
            futureOpenOrder.setTradingDay(tradingDay);
            futureOpenOrder.setTradeTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
            futureOpenOrder.setVolume(Integer.valueOf(volume));
            futureOpenOrder.setPrice(new BigDecimal(price).setScale(4, BigDecimal.ROUND_HALF_UP));
            futureOpenOrder.setOffsetFlag(offsetFlag);
            futureOpenOrder.setDirection(direction);
            futureOpenOrder.setTradeType(tradeType);
            futureOpenOrder.setOrderRef(orderRef);
            futureOpenOrder.setStatus(1);
            futureOpenOrder.setHoldVolume(Integer.valueOf(volume));
            futureOpenOrder.setCreateTime(new Date());
            futureOpenOrder.setUpdateTime(new Date());
            futureOpenOrder.setStopLossPoint(futureReportOrder.getStopLossPoint());
            futureOpenOrder.setEffectiveHoldVolume(Integer.valueOf(volume));//可用手数等于成功报单手数
            /**委托单笔开仓费用 总冻结资金/委托手数**/
            BigDecimal reportSinglePrice=futureReportOrder.getFrozenDeposit().divide(new BigDecimal(futureReportOrder.getVolume()));
            /**实际单笔开仓费用 开仓价*合约乘数**/
            BigDecimal actualSinglePrice=new BigDecimal(price).multiply(optionTradingGoods.getTradingContractMultiplier());
            /**委托撤回资金 (委托单笔开仓费用+开仓手续费)*实际成交手数**/
            BigDecimal reportWithdrawCapital=(reportSinglePrice.add(univalentOpenFee)).multiply(actualVolume);
            /**成交扣除资金 (实际单笔开仓费用+开仓手续费)*实际成交手数**/
            BigDecimal actualWithdrawCapital=(actualSinglePrice.add(univalentOpenFee)).multiply(actualVolume);
            /**开仓使用金额**/
            futureOpenOrder.setMargin(actualSinglePrice.multiply(actualVolume));
            futureOpenOrder.setOpenFee(univalentOpenFee.multiply(actualVolume));
            //futureOpenOrder.setStopLossAmount(stopLossAmount);
            futureOpenOrderMapper.insertSelective(futureOpenOrder);
            //AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(futureReportOrder.getCustomerId());
            /****************更新用户资金操作******************/
            AppCustomer appCustomer=new AppCustomer();
            appCustomer.setCustomerId(futureReportOrder.getCustomerId());
            //处理当前资金 资金=当前资金-成交扣除资金(实际单笔开仓费用+开仓手续费)*实际成交手数
            appCustomer.setCustomerCapital(-actualWithdrawCapital.doubleValue());
            //处理保证金 冻结资金=当前保证金-委托撤回资金(委托单笔开仓费用+开仓手续费)*实际成交手数
            appCustomer.setCustomerFreezeCapital(-reportWithdrawCapital.doubleValue());
//            LOGGER.info("respDealOrderOpen----->refreshCapital:" + appCustomer.toString());
            LOGGER.info(appCustomer.getCustomerTradeAccount()+"refreshCapital respDealOrderOpen:操作冻结" + appCustomer.getCustomerFreezeCapital()+"|操作资金"+appCustomer.getCustomerCapital());
            appCustomerMapper.refreshCapital(appCustomer);
            orderId=futureOpenOrder.getOpenId();
            orderType="1";
            /**封装成交推送数据包**/
            pushSocketModel.setInstrumentName(futureOpenOrder.getInstrumentName());
            pushSocketModel.setInstrumentCode(futureOpenOrder.getInstrumentCode());
            pushSocketModel.setLimitPrice(futureOpenOrder.getPrice());
            pushSocketModel.setTradeVolume(futureOpenOrder.getVolume());
            pushSocketModel.setStatusMsg("成功");
            pushSocketModel.setOrderNo(futureOpenOrder.getOrderRef());
            /**扣除机构冻结资金Begin**/
            /**委托撤回机构资金 (委托单笔开仓费用+开仓成本手续费)*实际成交手数**/
            BigDecimal reportCostWithdrawCapital=(reportSinglePrice.add(univalentCostOpenFee)).multiply(actualVolume);
            /**成交扣除机构资金 (实际单笔开仓费用+开仓成本手续费)*实际成交手数**/
            BigDecimal actualCostWithdrawCapital=(actualSinglePrice.add(univalentCostOpenFee)).multiply(actualVolume);
            tradeRemoteCall.tradeCapitalChange(businessInfoId,orderRef,"3","0",volume,actualCostWithdrawCapital.doubleValue(),reportCostWithdrawCapital.doubleValue());
            /**扣除机构冻结资金End**/
            /**推送资金记录开仓**/
            tradeRemoteCall.sendCashFlowToCrm(futureOpenOrder.getOpenId(),2,actualWithdrawCapital.doubleValue(),reportWithdrawCapital.doubleValue());
        }else{
            /**平仓回报**/
            FutureCloseOrder futureCloseOrder=futureCloseOrderMapper.selectByReportId(futureReportOrder.getReportId());
            futureCloseOrder.setCloseId(UUIDUtils.getUuid());
            futureCloseOrder.setCustomerId(futureReportOrder.getCustomerId());
            futureCloseOrder.setInvestorId(futureReportOrder.getInvestorId());
            futureCloseOrder.setProductCode(futureReportOrder.getProductCode());
            futureCloseOrder.setProductName(futureReportOrder.getProductName());
            futureCloseOrder.setInstrumentCode(futureReportOrder.getInstrumentCode());
            futureCloseOrder.setInstrumentName(futureReportOrder.getInstrumentName());
            futureCloseOrder.setOrderSysId(orderSysId);
            futureCloseOrder.setTradeId(tradeId);
            futureCloseOrder.setTradingDay(tradingDay);
            futureCloseOrder.setTradeTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
            futureCloseOrder.setVolume(Integer.valueOf(volume));
            futureCloseOrder.setClosePrice(new BigDecimal(price));

            /**计算平仓手续费**/
            BigDecimal closeFee=univalentCloseFee.multiply(new BigDecimal(volume));
            /**计算开仓手续费**/
            BigDecimal openFee=univalentOpenFee.multiply(new BigDecimal(volume));
            futureCloseOrder.setCloseFee(closeFee);
            futureCloseOrder.setOpenFee(openFee);
            /**查询平仓对应的开仓数据并修改开仓表中的当前持仓手数**/
            /**获取该笔平仓的开仓报单本地引用**/
            List<FutureOpenOrder> futureOpenOrderList=futureOpenOrderMapper.selectListByOrderRef(futureCloseOrder.getOpenOrderRef());
            Double sumMarketValue=0.00;
            Integer sumVolume=0;
            for(FutureOpenOrder obj:futureOpenOrderList){
                //计算市值
                BigDecimal marketValue=obj.getPrice().multiply(new BigDecimal(obj.getVolume()));
                sumMarketValue+=marketValue.doubleValue();
                sumVolume+=obj.getVolume();
            }
            /**计算持仓均价**/
            BigDecimal avgPrice=new BigDecimal(sumMarketValue/sumVolume).setScale(4, BigDecimal.ROUND_HALF_UP);
            /**计算平仓盈亏**/
            BigDecimal closeProfitLoss=null;
            /**平仓权利金=合约乘数*平仓价格*成交手数**/
            BigDecimal closePremium=optionTradingGoods.getTradingContractMultiplier().multiply(new BigDecimal(price)).multiply(actualVolume);
            /**开仓权利金=合约乘数*持仓均价*成交手数**/
            BigDecimal openPremium=optionTradingGoods.getTradingContractMultiplier().multiply(avgPrice).multiply(actualVolume);
            /**平仓盈亏=平仓权利金-开仓权利金**/
            closeProfitLoss=closePremium.subtract(openPremium);
            /**开仓价为持仓均价**/
            futureCloseOrder.setOpenPrice(avgPrice);
            futureCloseOrder.setCloseProfitLoss(closeProfitLoss);
            futureCloseOrder.setDirection(direction);
            futureCloseOrder.setTradeType(tradeType);
            futureCloseOrder.setOrderRef(orderRef);
            futureCloseOrder.setOffsetFlag(offsetFlag);
            futureCloseOrder.setCreateTime(new Date());
            futureCloseOrder.setUpdateTime(new Date());
            futureCloseOrder.setStatus(1);
            futureCloseOrder.setClosePremium(closePremium);
            futureCloseOrder.setOpenPremium(openPremium);
            //保存来自c++的平仓对象
            futureCloseOrderMapper.insertSelective(futureCloseOrder);
            this.buckleHoldVolume(futureCloseOrder.getOpenOrderRef(),futureCloseOrder.getVolume());
            /****************更新用户资金操作******************/
            AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(futureCloseOrder.getCustomerId());
            //处理当前资金 资金=当前资金+(平仓权利金-(平仓手续费*实际成交手数))
            Double closeActualCapital=closePremium.subtract(univalentCloseFee.multiply(actualVolume)).doubleValue();
            appCustomer.setCustomerCapital(closeActualCapital);
            //处理保证金 冻结资金=当前冻结资金-(平仓手续费*实际成交手数)
            Double closeFreezeCapital=(univalentCloseFee.multiply(actualVolume)).doubleValue();
            appCustomer.setCustomerFreezeCapital(-closeFreezeCapital);
//            LOGGER.info("respDealOrderClose----->refreshCapital:" + appCustomer.toString());
            LOGGER.info(appCustomer.getCustomerTradeAccount()+"refreshCapital respDealOrderClose:操作冻结" + appCustomer.getCustomerFreezeCapital()+"|操作资金"+appCustomer.getCustomerCapital());
            appCustomerMapper.refreshCapital(appCustomer);
            orderId=futureCloseOrder.getCloseId();
            orderType="2";
            /**封装成交推送数据包**/
            pushSocketModel.setInstrumentName(futureCloseOrder.getInstrumentName());
            pushSocketModel.setInstrumentCode(futureCloseOrder.getInstrumentCode());
            pushSocketModel.setLimitPrice(futureCloseOrder.getClosePrice());
            pushSocketModel.setTradeVolume(futureCloseOrder.getVolume());
            pushSocketModel.setOrderNo(futureCloseOrder.getOrderRef());
            pushSocketModel.setStatusMsg("成功");
            /**添加机构冻结资金Begin!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**/
            //处理当前资金 资金=当前资金+(平仓权利金-(平仓成本手续费*实际成交手数))
            Double closeCostActualCapital=closePremium.subtract(univalentCostCloseFee.multiply(actualVolume)).doubleValue();
            //处理保证金 冻结资金=当前冻结资金-(平仓成本手续费*实际成交手数)
            Double closeCostFreezeCapital=(univalentCostCloseFee.multiply(actualVolume)).doubleValue();
            tradeRemoteCall.tradeCapitalChange(businessInfoId,orderRef,"4","0",volume,closeCostActualCapital,closeCostFreezeCapital);
            /**添加机构冻结资金End**/
            /**推送资金记录平仓**/
            tradeRemoteCall.sendCashFlowToCrm(futureCloseOrder.getCloseId(), 3,closeActualCapital,closeFreezeCapital);
        }
        sendDealOrderToCrm(orderId, orderType);
        /**成交 socket推送**/
        sendNotifyChannel(futureReportOrder.getCustomerId(),Constant.DEAL_ORDER,pushSocketModel);
        return ResponseEntity.ok(Message.REQ_SUCCESS);
    }

    /**
     * 递归去扣持仓手数
     * @param orderRef
     * @param closeVolume
     */
    private void buckleHoldVolume(String orderRef,int closeVolume){
        Map<String,Object> map=new HashMap<>();
        map.put("orderRef",orderRef);
        map.put("sort","ASC");
        map.put("type",2);
        FutureOpenOrder futureOpenOrder = this.getNotLockFutureOpenOrder(map, 10,"buckleHoldVolume");
        LOGGER.info("getHoldVolume:" + futureOpenOrder.getHoldVolume() + ";closeVolume:" + closeVolume);
        Map<String,Object> mapParameter=new HashMap<String,Object>();
        int difference=futureOpenOrder.getHoldVolume()-closeVolume;
        /**差值==0不需要递归 刚好扣完**/
        if(difference<=0){
            mapParameter.put("status", 3);//全部平仓
            //扣除平仓手数
            mapParameter.put("holdVolume", -futureOpenOrder.getHoldVolume());
//            mapParameter.put("stopLossAmount",futureOpenOrder.getStopLossAmount().multiply(new BigDecimal(-1)));
//            mapParameter.put("margin", futureOpenOrder.getMargin().multiply(new BigDecimal(-1)));
            mapParameter.put("openId", futureOpenOrder.getOpenId());
            futureOpenOrderMapper.updateCalculationData(mapParameter);
            /**释放锁**/
            redisTemplate.delete(Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
            LOGGER.info("buckleHoldVolume difference<=0 Release lock Success---->" + Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
            if(difference<0){
                this.buckleHoldVolume(orderRef, Math.abs(difference));
            }
        }
        //部分平仓
        if(difference>0){
            mapParameter.put("status",2);//部分平仓
            //扣除平仓手数
            mapParameter.put("holdVolume", -closeVolume);
//            mapParameter.put("margin", new BigDecimal(cashDeposit).multiply(new BigDecimal(-1)));
            mapParameter.put("openId", futureOpenOrder.getOpenId());
            futureOpenOrderMapper.updateCalculationData(mapParameter);
            /**释放锁**/
            redisTemplate.delete(Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
            LOGGER.info("buckleHoldVolume difference>0 Release lock Success---->" + Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
        }
    }
    /**
     * 递归去扣可用手数
     * @param orderRef
     * @param closeVolume
     */
    public void buckleEffectiveHoldVolume(String orderRef,int closeVolume){
        Map<String,Object> map=new HashMap<>();
        map.put("orderRef",orderRef);
        map.put("sort", "ASC");
        map.put("type", 1);
        FutureOpenOrder futureOpenOrder=this.getNotLockFutureOpenOrder(map, 10,"buckleEffectiveHoldVolume");
        int difference=futureOpenOrder.getEffectiveHoldVolume()-closeVolume;
        if(difference>=0){
            futureOpenOrder.setEffectiveHoldVolume(-closeVolume);
            futureOpenOrderMapper.updateEffectiveHoldVolume(futureOpenOrder);
            /**释放锁**/
            redisTemplate.delete(Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
            LOGGER.info("buckleEffectiveHoldVolume difference>=0 Release lock Success---->" + Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
        }
        if(difference<0){
            futureOpenOrder.setEffectiveHoldVolume(-futureOpenOrder.getEffectiveHoldVolume());
            futureOpenOrderMapper.updateEffectiveHoldVolume(futureOpenOrder);
            /**释放锁**/
            redisTemplate.delete(Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
            LOGGER.info("buckleEffectiveHoldVolume difference<0 Release lock Success---->" + Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
            this.buckleEffectiveHoldVolume(orderRef, Math.abs(difference));

        }
    }
    /**
     * 撤回时 递归去回复刚才扣除的可用手数
     * @param orderRef
     * @param withdrawVolume
     */
    private void replyEffectiveHoldVolume(String orderRef,int withdrawVolume){
        Map<String,Object> map=new HashMap<>();
        map.put("orderRef", orderRef);
        map.put("sort", "DESC");
        map.put("type", 1);
        FutureOpenOrder futureOpenOrder=this.getNotLockFutureOpenOrder(map, 10,"replyEffectiveHoldVolume");
        if(futureOpenOrder!=null){
            LOGGER.info(orderRef + "replyEffectiveHoldVolume info---->" + futureOpenOrder.getHoldVolume() + "|" + futureOpenOrder.getEffectiveHoldVolume());
            int difference=futureOpenOrder.getHoldVolume()-futureOpenOrder.getEffectiveHoldVolume();
            if(difference>=withdrawVolume){
                //加上去 不再递归
                futureOpenOrder.setEffectiveHoldVolume(withdrawVolume);
                futureOpenOrderMapper.updateEffectiveHoldVolume(futureOpenOrder);
                /**释放锁**/
                redisTemplate.delete(Constant.FUTUREOPENORDER+futureOpenOrder.getOpenId());
                LOGGER.info("replyEffectiveHoldVolume difference>=withdrawVolume Release lock Success---->"+Constant.FUTUREOPENORDER+futureOpenOrder.getOpenId());
            }
            if(difference<withdrawVolume){
                //加上去 继续递归
                futureOpenOrder.setEffectiveHoldVolume(difference);
                futureOpenOrderMapper.updateEffectiveHoldVolume(futureOpenOrder);
                /**释放锁**/
                redisTemplate.delete(Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
                LOGGER.info("replyEffectiveHoldVolume difference<withdrawVolume Release lock Success---->" + Constant.FUTUREOPENORDER + futureOpenOrder.getOpenId());
                this.replyEffectiveHoldVolume(orderRef, Math.abs(difference - withdrawVolume));
            }
        }else{
            LOGGER.info(orderRef + "replyEffectiveHoldVolume---->futureOpenOrder Is null");
        }
    }

    /**
     * 获取没有被锁的持仓对象
     * @param map
     * @return
     */
    private FutureOpenOrder getNotLockFutureOpenOrder(Map<String,Object> map,int version,String type){
        FutureOpenOrder futureOpenOrder=null;
        try {
            futureOpenOrder=futureOpenOrderMapper.selectByOrderRef(map);
            if(futureOpenOrder!=null){
                LOGGER.info("getNotLockFutureOpenOrder futureOpenOrder is not null--->"+futureOpenOrder.getOpenId()+"|"+type);
                String lockKey=Constant.FUTUREOPENORDER+futureOpenOrder.getOpenId();
                /**对开仓订单上锁**/
                boolean flag=redisTemplate.opsForValue().setIfAbsent(lockKey, "lock");
                if(flag){
                    LOGGER.info("getNotLockFutureOpenOrder Success access to lock--->"+futureOpenOrder.getOpenId()+"|"+type);
                    /**对设置的锁 设失效时间**/
                    redisTemplate.expire(lockKey, 4, TimeUnit.SECONDS);
                }else{
                    LOGGER.info("getNotLockFutureOpenOrder No access to lock--->"+futureOpenOrder.getOpenId()+"|"+type);
                    /**说明已经被锁了 sleep 1s 之后接着查**/
                    if(version>0){
                        Thread.sleep(500);
                        version--;
                        futureOpenOrder=this.getNotLockFutureOpenOrder(map,version,type);
                    }
                }
            }
            return  futureOpenOrder;
        }catch (Exception e){
            LOGGER.error("getNotLockFutureOpenOrder ERROR--------->",e);
            return  futureOpenOrder;
        }
    }


    @Async("executorPool")
    @Override
    public void sendNotifyChannel(String customerId,Integer type,Object data){
        try{
            //推送报单最新状态
            Map<String,Object> map=new HashMap<>();
            SocketJsonData socketJsonData=new SocketJsonData(type,data);
            Object futureReportOrderToJson= JSONObject.toJSON(socketJsonData);
            map.put("uuid",customerId);
            map.put("data", futureReportOrderToJson);
            LOGGER.info("将客户uuid为：" + customerId + ",发布到redis中；数据对象为：" + map);
            Object mapToJson= JSONObject.toJSON(map);
            redisTemplate.convertAndSend(Constant.TRADING_NOTIFY_CHANNEL, mapToJson.toString());
        }catch (Exception e){
            LOGGER.error("推送成交订单状态异常了.............",e);
        }
    }

    @Async("executorPool")
    @Override
    public void sendDealOrderToCrm(String orderId,String orderType){
        try {
            Map<String,String> map=new HashMap<String, String>();
            map.put("id",orderId);
            //1-开仓 2-平仓
            map.put("type", orderType);
            Object json=JSONObject.toJSON(map);
            LOGGER.info("sendDealOrderToCrm............info:" + json.toString());
            redisTemplate.convertAndSend(Constant.FUTURE_BUSINESS, json.toString());
        }catch (Exception e){
            LOGGER.error("成交单send管理端失败...........",e);
        }
    }

    @Override
    public void sendPositionOrder() {
        try {
            /**查询持仓单**/
            List<FutureOpenOrder> futureOpenOrderList=futureOpenOrderMapper.selectPositionOrder(new FutureOpenOrder());
            /**开始推送持仓数据**/
            this.sendPositionOrderToC(futureOpenOrderList);
            /**查询母账户信息**/
            List<OptionAccount> optionAccountList=optionAccountMapper.selectAll(new OptionAccount());
            /**开始推送母账户信息**/
            this.sendOptionAccountToC(optionAccountList);
            /**查询创建时间为当天且收到响应且成交数量 < 委托数量**/
            List<FutureReportOrder> futureReportOrderList=futureReportOrderMapper.selectCancelNeedOrderInfo();
            /**开始推送委托订单数据**/
            this.sendCancelNeedOrderInfo(futureReportOrderList);
        }catch (Exception e){
            LOGGER.error("sendPositionOrderAndChg error.............",e);
        }
    }

    /**
     * 获取 持仓/委托/平仓列表
     * @param optionTradeDto 1-持仓 2-委托 3-平仓
     * @return
     */
    @Override
    public List<?> getOrderList(OptionTradeDto optionTradeDto) {
        List<?> list = null;
        Map<String,Object> map=new HashMap<String,Object>();
        map.put("customerId",optionTradeDto.getCustomerId());
        String nowDate=DateUtils.convertDateToString(new Date());
        if(optionTradeDto.getReqsOrderType()==1){
            map.put("beginCreateTime",optionTradeDto.getBeginCreateTime());
            map.put("endCreateTime",optionTradeDto.getEndCreateTime());
            List<OpenOrderVo> openOrderVos=futureOpenOrderMapper.selectOpenOrderByPage(map);
            for(OpenOrderVo obj:openOrderVos){
//                FuturesTradingGoods futuresTradingGoods=futuresTradingGoodsMapper.selectByTradingGoodsCode(obj.getProductCode());
                OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(obj.getProductCode());
                obj.setContractMultiplier(optionTradingGoods.getTradingContractMultiplier().intValue());
                Object optionSymbolObj=getRedisSymbol(nowDate, obj.getInstrumentCode(),7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, obj.getInstrumentCode());
                OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
                //key=OPTION-TICK-INFO:510050:201811:2018-11-08
//                String tickKey=Constant.OPTION_TICKL_INFO+obj.getProductCode()+":"+optionSymbol.getInstrumentMonth()+":"+nowDate;
                //(String)redisTemplate.opsForHash().get(tickKey,obj.getInstrumentCode());
                String optionTickStr=(String)getRedisTick(obj.getProductCode(),optionSymbol.getInstrumentMonth(),nowDate,obj.getInstrumentCode(),7);
                JSONObject json = JSONObject.parseObject(optionTickStr);
                String lastPrice=json.get("lastPrice").toString();
                ///申买价一
                String bidPrice1=json.get("bidPrice1").toString();
                ///申卖价一
                String askPrice1=json.get("askPrice1").toString();
                obj.setLastPrice(lastPrice);
                obj.setBidPrice1(bidPrice1);
                obj.setAskPrice1(askPrice1);
                obj.setInstrumentMonth(optionSymbol.getInstrumentMonth());
                obj.setTradingGoodsCode(optionSymbol.getTargetCode());
                //查询风控提前天数
                OptionTradingContract optionTradingContract=new OptionTradingContract();
                optionTradingContract.setTradingGoodsId(optionTradingGoods.getTradingGoodsId());
                optionTradingContract.setContractMonthName(optionSymbol.getInstrumentMonth());
                List<OptionTradingContract> optionTradingContractList=optionTradingContractMapper.selectByWhere(optionTradingContract);
                if(optionTradingContractList.size()>0){
                    try {
                        Date instrumentExpireDay=DateUtils.minusDay(DateUtils.convertStringToDate(optionSymbol.getExpireDay()),
                                optionTradingContractList.get(0).getForceCloseAdvanceDay());
                        obj.setInstrumentExpireDay(DateUtils.convertDateToString(instrumentExpireDay)+" 14:00:00");
                    }catch (Exception e){
                        LOGGER.error("convertStringToDateError getOrderList...........");
                    }

                }
//                LOGGER.info("obj.setSumMarkeValue:" + obj.getSumMarkeValue());
            }
            list=openOrderVos;
        }else if(optionTradeDto.getReqsOrderType()==2){
            String beginTime=DateUtils.getTradeTime();
            map.put("pageIndex", optionTradeDto.getPageNum());
            map.put("pageSize",optionTradeDto.getPageSize());
            map.put("beginCreateTime",beginTime);//DateUtils.parseToDateTimeStr(DateUtils.minusDay(new Date(),0))
            map.put("endCreateTime", DateUtils.parseToDateTimeStr(new Date()));
            List<FutureReportOrder> futureReportOrders=futureReportOrderMapper.selectReportOrderByPage(map);
            for(FutureReportOrder obj:futureReportOrders){
                Object optionSymbolObj=getRedisSymbol(nowDate, obj.getInstrumentCode(),7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, obj.getInstrumentCode());
                OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
                obj.setInstrumentMonth(optionSymbol.getInstrumentMonth());
            }
            list=futureReportOrders;
        }else if(optionTradeDto.getReqsOrderType()==3){
            String beginTime=DateUtils.getTradeTime();
            map.put("pageIndex",optionTradeDto.getPageNum());
            map.put("pageSize",optionTradeDto.getPageSize());
            if(StringUtils.isNotBlank(optionTradeDto.getBeginCreateTime()) && StringUtils.isNotBlank(optionTradeDto.getEndCreateTime())){
                map.put("beginCreateTime",optionTradeDto.getBeginCreateTime());
                map.put("endCreateTime",optionTradeDto.getEndCreateTime());
            }else{
                map.put("beginCreateTime",beginTime);
                map.put("endCreateTime",DateUtils.parseToDateTimeStr(new Date()));
            }
            list=futureCloseOrderMapper.selectCloseOrderByPage(map);
        }
        return list;
    }

    public void sendPositionOrderToC(List<FutureOpenOrder> futureOpenOrderList){
        try{
            SocketHandler socketHandler= null;
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            for(FutureOpenOrder obj:futureOpenOrderList){
                BigDecimal targetProfitPoint=obj.getTargetProfitPoint()==null?new BigDecimal(0):obj.getTargetProfitPoint();
                BigDecimal stopLossPoint=obj.getStopLossPoint()==null?new BigDecimal(0):obj.getStopLossPoint();
                //拼接给C++的字符串 positionOrder|期货账户|子账户|本地报单委托|开仓价|止盈点|止损点|当前持仓手数|买卖方向|合约code
                String dataStr="positionOrder|"+obj.getInvestorId()+"|"+obj.getCustomerTradeAccount()+"|"+obj.getOrderRef()+"|"+obj.getPrice()+"|"
                        +targetProfitPoint+"|"+stopLossPoint+"|"+obj.getHoldVolume()+"|"+obj.getDirection()+"|"+obj.getInstrumentCode();
                /**判断是否断开连接，断开返回true,没有返回false**/
                boolean flag=socketHandler.isServerClose();
                if(flag){
                    //连接断开 结束本次循环
                    continue;
                }
                LOGGER.info("sendPositionOrderToC--->" + dataStr);
                socketHandler.sendMessage(dataStr);
                /**延迟10ms**/
                Thread.sleep(10);
            }
        }catch (Exception e){
            LOGGER.error("sendPositionOrderToC connection socket err ....." , e);
        }

    }

    public void sendOptionAccountToC(List<OptionAccount> optionAccounts){
        try{
            SocketHandler socketHandler= null;
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            for(OptionAccount obj:optionAccounts){   //
                //拼接给C++的字符串 optionAccount|开户公司|交易母账户|开户手机号|交易密码|当前状态(状态：0-当前启用、1-当前停用)
                String dataStr="optionAccount|"+obj.getOptionAccountCompany()+"|"+obj.getOptionTradeAccount()+"|"+obj.getOptionTradeMobile()+"|"+obj.getOptionTradePassword()
                        +"|"+obj.getAccountStatus();
                /**判断是否断开连接，断开返回true,没有返回false**/
                boolean flag=socketHandler.isServerClose();
                if(flag){
                    //连接断开 结束本次循环
                    continue;
                }
                LOGGER.info("sendOptionAccountToC-->:" + dataStr);
                socketHandler.sendMessage(dataStr);
                /**延迟10ms**/
                Thread.sleep(10);
            }
        }catch (Exception e){
            LOGGER.error("sendPositionOrderToC connection socket err ....." , e);
        }

    }

    /**
     * 推送创建时间为当天且 收到响应 且 成交数量 < 委托数量 且 订单最后状态不是 全部成交|撤单|错单
     * @param futureReportOrderList
     */
    public void sendCancelNeedOrderInfo(List<FutureReportOrder> futureReportOrderList){
        try{
            LOGGER.info("sendCancelNeedOrderInfo futureReportOrderList.list--->"+futureReportOrderList.size());
            SocketHandler socketHandler= null;
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            for(FutureReportOrder obj:futureReportOrderList){   //
                AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(obj.getCustomerId());
                String [] orderStatusAry=obj.getOrderStatus().split("\\|");
                String lastOrderStatus=orderStatusAry[orderStatusAry.length-1];
                /**客户信息不等于空 最后状态不等于全部成交 不等于错单 不等于撤单 不等于（交易程序来自qwin 并且最后状态等于a）**/
                if(appCustomer != null && !"0".equals(lastOrderStatus) && !"99".equals(lastOrderStatus) && !"5".equals(lastOrderStatus) && !("3".equals(obj.getTradeServerCode()) && "a".equals(lastOrderStatus))){
                    //拼接给C++的字符串 orderInfo|母账户|子账户|本地委托|合约|委托价|委托数量|成交数量|买卖方向|开平方向|系统OsID|报单类型(0普通下单.1对冲下单.2跟单 )|是否隔夜(0不隔夜.1隔夜)|
                    String dataStr="orderInfo|"+obj.getInvestorId()+"|"+appCustomer.getCustomerTradeAccount()+"|"+obj.getOrderRef()+"|"+obj.getInstrumentCode()+"|"+obj.getLimitPrice()
                            +"|"+obj.getVolume()+"|"+obj.getTradeVolume()+"|"+obj.getDirection()+"|"+obj.getOffsetFlag()+"|"+obj.getOrderSysId()+"|"+obj.getOrderType()+"|0";
                    /**判断是否断开连接，断开返回true,没有返回false**/
                    boolean flag=socketHandler.isServerClose();
                    if(flag){
                        //连接断开 结束本次循环
                        continue;
                    }
                    LOGGER.info("sendCancelNeedOrderInfo dataInfo-->" + dataStr);
                    socketHandler.sendMessage(dataStr);
                    /**延迟10ms**/
                    Thread.sleep(10);
                }else{
                    LOGGER.info("sendCancelNeedOrderInfo Check failed-------------->"+obj.getOrderRef());
                }
            }
        }catch (Exception e){
            LOGGER.error("sendCancelNeedOrderInfo Error ....." , e);
        }

    }

    /**
     *
     * @param remainderDays 合约交割到期天数
     * @param forceCloseAdvanceDay 强平提前天数
     * @return
     */
    private boolean checkInRiskManagement(Integer remainderDays,Integer forceCloseAdvanceDay){
        try {
            /**交割到期天数==强平提前天数 当天14：00无法开仓**/
            if(remainderDays==forceCloseAdvanceDay){
                Date riskManagementDate=DateUtils.convertStringToDate("HH:mm","14:00");
                Date nowDate= DateUtils.convertStringToDate("HH:mm", DateUtils.getDateTime("HH:mm", new Date()));
                /**当前时间大于风控的时间 则进入风控不可开仓**/
                if(nowDate.after(riskManagementDate)){
                    return false;
                }
            }
            /**到期天数<强平提前天数 0<1 则无法开仓**/
            if(remainderDays<forceCloseAdvanceDay){
                return false;
            }
        }catch (Exception e){
            LOGGER.error("checkInRiskManagement.......",e);
            return false;
        }
        return true;
    }



    private Object getRedisTick(String targetCode,String instrumentMonth,String today,String contraceCode,int version){
        try{
            String tickKey=Constant.OPTION_TICKL_INFO+targetCode+":"+instrumentMonth+":"+today;
            Object optionTick=redisTemplate.opsForHash().get(tickKey,contraceCode);
            if(optionTick==null && version>0){
                Date todayDate=DateUtils.minusDay(DateUtils.convertStringToDate(today), 1);
                version--;
                optionTick = this.getRedisTick(targetCode, instrumentMonth, DateUtils.convertDateToString(todayDate), contraceCode, version);
            }
            return optionTick;
        }catch (Exception e){
            LOGGER.error("getRedisTickError........"+e);
            return null;
        }

    }

    /**
     * 校验当前时间是否在交易时间段内
     * @param tradingTime
     * @return
     */
    public boolean checkTradingTime(String tradingTime){
        boolean flag=false;
        try {
            Date nowDate= DateUtils.convertStringToDate("yyyy-MM-dd HH:mm", DateUtils.parseToDateTimeStr(new Date()));
            Integer currentTimeHour=Integer.valueOf(DateUtils.getDateTime("HH", nowDate));
            //如果第二天为节假日 则晚盘不开
            /**dayType 工作日对应结果为 0, 休息日对应结果为 1, 节假日对应的结果为 2**/
            if(currentTimeHour>=21){
                //大于21点 判断明天是否是节假日
                Response tomorrowDay=tradeRemoteCall.checkHoliday(DateUtils.convertDateToString(DateUtils.addDay(nowDate, 1)));
                if(tomorrowDay.isSuccess() && tomorrowDay.getDayType()==2){
                    return flag;
                }
            }
            Response response=tradeRemoteCall.checkHoliday(DateUtils.convertDateToString(nowDate));
            /**如果是周六 则00：00~02：30是可以交易的**/
            if( response.isSuccess() && response.getDayType()==1){
                //如果是周六 除00:00~02:30 其他时间都是非交易时间  02:30~03:00交给时间段控制
                if(currentTimeHour>=3 && response.getWeek()==6){
                    return flag;
                }
                //如果是周日 则直接提示 非交易时间
                if(response.getWeek()==0){
                    return flag;
                }
            }
            //如果是工作日 则直接校验时间段
            if(response.isSuccess() && (response.getDayType()==0 || response.getWeek()==6)){
                String [] tradingTimes=tradingTime.split("\\|");
                for(String time:tradingTimes){
                    String [] timeSlot = time.split("-");
                    Date beginTime=null,endTime=null;
                    String nowDateStr=DateUtils.convertDateToString(new Date());
                    beginTime= DateUtils.convertStringToDate("yyyy-MM-dd HH:mm", nowDateStr + " " + timeSlot[0]);
                    Integer beginHour=Integer.valueOf(DateUtils.getDateTime("HH", beginTime));
                    endTime=DateUtils.convertStringToDate("yyyy-MM-dd HH:mm",nowDateStr+" "+timeSlot[1]);
                    Integer endTimeHour=Integer.valueOf(DateUtils.getDateTime("HH", endTime));
                    if(beginHour==21 && endTimeHour<23){
                        if(currentTimeHour>3){
                            endTime=DateUtils.addDay(endTime,1);
                        }else{
                            beginTime=DateUtils.addDay(beginTime,-1);
                        }
                    }
                    flag=DateUtils.belongCalendar(nowDate,DateUtils.addMinute(beginTime,-1),endTime);
                    if(flag){
                        break;
                    }
                }
            }
        } catch (ParseException e) {
            LOGGER.error("checkTradingTime......."+e);
        }finally {
            return flag;
        }
    }

    /**
     * 校验机构户资金是否充足
     * @param sumTradeAmount
     * @return true 充足 false 不足
     */
    private Map<String,Object> checkBusinessCapital(Double sumTradeAmount){
        Map<String,Object> map=new HashMap<String,Object>();
        boolean flag=false;
        String version="";
        AgencyBusiness agencyBusiness=agencyBusinessMapper.selectByBusinessLevel();
        String jsonResult=tradeRemoteCall.getAgencyAccount(agencyBusiness.getUniqueIdentification());
        JSONObject result2json=JSONObject.parseObject(jsonResult);
        Object jsonData=result2json.get("data");
        JSONObject data2json=JSONObject.parseObject(jsonData.toString());
        /**当前总资金**/
        Double businessCapital=data2json.getDouble("businessCapital")==null?0.00:data2json.getDouble("businessCapital");
        /**当前总冻结资金**/
        Double businessFreezeCapital=data2json.getDouble("businessFreezeCapital")==null?0.00:data2json.getDouble("businessFreezeCapital");
        /**可用资金**/
        Double availableCapital=businessCapital-businessFreezeCapital;
        /**下单总金额小于等于机构户可用资金**/
        if(sumTradeAmount<=availableCapital){
            flag=true;
            version=data2json.getString("version")==null?"0":data2json.getString("version");
            map.put("version",version);
            map.put("businessInfoId",agencyBusiness.getUniqueIdentification());
        }
        map.put("flag",flag);
        return map;
    }

    /**
     * 风控强平
     * @param instrumentCode
     */
    @Override
    public void riskManagementClose(String instrumentCode,Double limitPrice) {
        try {
            FutureOpenOrder futureOpenOrderByWhere=new FutureOpenOrder();
            futureOpenOrderByWhere.setInstrumentCode(instrumentCode);
            List<FutureOpenOrder> futureOpenOrderList=futureOpenOrderMapper.selectPositionOrder(futureOpenOrderByWhere);
            Object hedgingStatusObj=redisTemplate.opsForValue().get(Constant.HEDGING_STATUS);
            Integer hedgingStatus=hedgingStatusObj==null?1:JSONObject.parseObject(hedgingStatusObj.toString()).getInteger("hedgingStatus");
            /**对冲单状态  0-开启 1-关闭**/
            for(FutureOpenOrder futureOpenOrder:futureOpenOrderList){
                if(futureOpenOrder.getEffectiveHoldVolume()>0){
                    OptionTradeDto optionTradeDto=new OptionTradeDto();
                    optionTradeDto.setContraceCode(futureOpenOrder.getInstrumentCode());
                    optionTradeDto.setCustomerId(futureOpenOrder.getCustomerId());
                    optionTradeDto.setDirection("1");
                    optionTradeDto.setLimitPrice(limitPrice);
                    //设置风控强平
                    optionTradeDto.setOffsetFlag("4");
                    optionTradeDto.setOpenOrderRef(futureOpenOrder.getOrderRef());
                    /**市价方式去委托风控单**/
                    optionTradeDto.setOrderPriceType(0);
                    if(futureOpenOrder.getEffectiveHoldVolume()<=10){
                        optionTradeDto.setVolume(futureOpenOrder.getEffectiveHoldVolume());
                        ResponseEntity<Message> responseEntity=hedgingStatus==0?this.reqsHedgingReportOrder(optionTradeDto):this.reqsCloseReportOrder(optionTradeDto);
                        LOGGER.info(futureOpenOrder.getOrderRef() + ":responseEntity--->1" + responseEntity.getBody().getMsg());
                    }else{
                        /**如果大于10张的话 就需要进行拆单平仓**/
                        /**整除数**/
                        Integer dismantleNum=futureOpenOrder.getEffectiveHoldVolume()/Constant.MARKET_MAX_NUM;
                        /**余数**/
                        Integer remainder=futureOpenOrder.getEffectiveHoldVolume()%Constant.MARKET_MAX_NUM;
                        for(int i=0;i<dismantleNum;i++){
                            optionTradeDto.setVolume(Constant.MARKET_MAX_NUM);
                            ResponseEntity<Message> responseEntity=hedgingStatus==0?this.reqsHedgingReportOrder(optionTradeDto):this.reqsCloseReportOrder(optionTradeDto);
                            LOGGER.info(futureOpenOrder.getOrderRef() + ":responseEntity--->2" + responseEntity.getBody().getMsg());
                        }
                        if(remainder>0){
                            optionTradeDto.setVolume(remainder);
                            ResponseEntity<Message> responseEntity=hedgingStatus==0?this.reqsHedgingReportOrder(optionTradeDto):this.reqsCloseReportOrder(optionTradeDto);
                            LOGGER.info(futureOpenOrder.getOrderRef() + ":responseEntity--->3" + responseEntity.getBody().getMsg());
                        }
                    }
                }else{
                    LOGGER.info("riskManagementClose存在全部挂单--->报单编号："+futureOpenOrder.getOrderRef()+"|当前持仓数："+futureOpenOrder.getHoldVolume()+"|当前可用持仓数："+futureOpenOrder.getEffectiveHoldVolume());
                }
            }
        }catch (Exception e){
            LOGGER.error("riskManagementCloseError............",e);
        }
    }

    /**
     * 对冲单报单请求
     * @param optionTradeDto
     * @return
     */
    @Transactional
    @Override
    public ResponseEntity<Message> reqsHedgingReportOrder(OptionTradeDto optionTradeDto) throws Exception {
        AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(optionTradeDto.getCustomerId());
        /**获取合约信息**/
        String nowDate= DateUtils.convertDateToString(new Date());
        Object optionSymbolObj=getRedisSymbol(nowDate,optionTradeDto.getContraceCode(),7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, optionTradeDto.getContraceCode());
        OptionSymbol optionSymbol= JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
        LOGGER.warn("optionSymbol info....."+optionSymbol.toString());
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionSymbol.getTargetCode());
        if(optionTradingGoods==null){
            /**当前标的不存在**/
            return ResponseEntity.ok(Message.TARGET_NOT_EXIST);
        }
        /**是否在交易时间内**/
        boolean checkTrading=this.checkTradingTime(optionTradingGoods.getTradingTime());
        if(!checkTrading){
            //当前非交易时间
            return ResponseEntity.ok(Message.NOT_TRADING_TIME);
        }
        //校验该用户是否有平仓手数
        List<FutureOpenOrder> futureOpenOrderList=futureOpenOrderMapper.selectListByOrderRef(optionTradeDto.getOpenOrderRef());
        /**判断认证状态 0-未认证 1-已认证 2-认证失败**/
        if(appCustomer.getAuthenticationStatus()!=1){
            //未认证
            return ResponseEntity.ok(Message.CUSTOMER_IS_NOT_AUTHENTICATION);
        }
        if(futureOpenOrderList==null){
            return ResponseEntity.ok(Message.ARGS_ERROR);
        }
        Integer effectiveHoldVolume=0;
        for(FutureOpenOrder obj:futureOpenOrderList){
            //平仓用户与开仓用户不匹配
            if(!obj.getCustomerId().equals(appCustomer.getCustomerId())){
                return ResponseEntity.ok(Message.CUSTOMER_MATCHING_ERROR);
            }
            //平仓的合约于开仓的合约code不一致
            if(!obj.getInstrumentCode().equals(optionTradeDto.getContraceCode())){
                return ResponseEntity.ok(Message.CONTRACECODE_MATCHING_ERROR);
            }
            effectiveHoldVolume=effectiveHoldVolume+obj.getEffectiveHoldVolume();
        }
        //可用手数<平仓手数 提示平仓手数不足
        if(effectiveHoldVolume<optionTradeDto.getVolume()){
            return ResponseEntity.ok(Message.HOLD_VOLUME_INSUFFICIENT);
        }
        /**由那个账户开仓的 由那个账户平仓**/
        FutureOpenOrder futureOpenOrder=futureOpenOrderList.get(0);
        OptionAccount optionAccount=optionAccountMapper.selectAccountByStatus(0,1);
        /**交易账户信息**/
        if(optionAccount==null){
            /**交易账户异常**/
            return ResponseEntity.ok(Message.TRADE_ACCOUNT_ERROR);
        }
        //查询开仓报单时的开仓手续费和平仓手续费
        FutureReportOrder openReportOrder=futureReportOrderMapper.selectByOrderRef(optionTradeDto.getOpenOrderRef(), optionTradeDto.getContraceCode());
        //拼接委托对象数据
        FutureReportOrder futureReportOrder=new FutureReportOrder();
        futureReportOrder.setReportId(UUIDUtils.getUuid());
        futureReportOrder.setCustomerId(optionTradeDto.getCustomerId());
        futureReportOrder.setInvestorId(futureOpenOrder.getInvestorId());
        futureReportOrder.setProductCode(optionSymbol.getTargetCode());
        futureReportOrder.setProductName(optionTradingGoods.getTradingGoodsName());
        futureReportOrder.setInstrumentCode(optionTradeDto.getContraceCode());
        futureReportOrder.setInstrumentName(optionSymbol.getInstrumentName());
        futureReportOrder.setOrderRef(getOrderRefStr());
        futureReportOrder.setUnivalentOpenFee(openReportOrder.getUnivalentOpenFee());
        futureReportOrder.setUnivalentCloseFee(openReportOrder.getUnivalentCloseFee());
        futureReportOrder.setUnivalentCostOpenFee(openReportOrder.getUnivalentCostOpenFee());
        futureReportOrder.setUnivalentCostCloseFee(openReportOrder.getUnivalentCostCloseFee());
        BigDecimal closePrice=new BigDecimal(optionTradeDto.getLimitPrice()).setScale(4,BigDecimal.ROUND_HALF_UP);
        futureReportOrder.setLimitPrice(closePrice);
        futureReportOrder.setDirection(optionTradeDto.getDirection().toString());
        futureReportOrder.setOffsetFlag(optionTradeDto.getOffsetFlag().toString());
        futureReportOrder.setVolume(optionTradeDto.getVolume());
        futureReportOrder.setCreateTime(new Date());
        futureReportOrder.setUpdateTime(new Date());
        /**补充报单价格类型**/
        futureReportOrder.setOrderPriceType(optionTradeDto.getOrderPriceType());
        /**补充为对冲单**/
        futureReportOrder.setHedgingFlag(1);
        /**平仓报单录入数据**/
        futureReportOrderMapper.insertSelective(futureReportOrder);
        /**对冲卖开单报单数据录入**/
        //String reportId, String customerId, String investorId, String productCode, String productName, String instrumentCode, String instrumentName, String orderRef, BigDecimal univalentOpenFee, BigDecimal univalentCloseFee,
        // BigDecimal univalentCostOpenFee, BigDecimal univalentCostCloseFee, BigDecimal limitPrice, String direction, String offsetFlag
        /**发起对冲指令 开平标识 ( 0 : 建仓 ,1 : 平仓 , 2 :止盈平仓，3：止损平仓，4-定时强平，7-涨跌幅强平)**/
        Integer hedgingOffsetFlag=Integer.valueOf(futureReportOrder.getOffsetFlag())==0?1:0;
        HedgingReportOrder hedgingReportOrder=new HedgingReportOrder(
                UUIDUtils.getUuid(), optionTradeDto.getCustomerId(),futureOpenOrder.getInvestorId(),optionSymbol.getTargetCode(),optionTradingGoods.getTradingGoodsName(),
                optionTradeDto.getContraceCode(),optionSymbol.getInstrumentName(),futureReportOrder.getOrderRef(),openReportOrder.getUnivalentOpenFee(),openReportOrder.getUnivalentCloseFee(),
                openReportOrder.getUnivalentCostOpenFee(),openReportOrder.getUnivalentCostCloseFee(),futureReportOrder.getLimitPrice(),futureReportOrder.getDirection(),String.valueOf(hedgingOffsetFlag),optionTradeDto.getOrderPriceType());
        hedgingReportOrderMapper.insertSelective(hedgingReportOrder);
        //递归去扣可用手数
        this.buckleEffectiveHoldVolume(optionTradeDto.getOpenOrderRef(), optionTradeDto.getVolume());
        //修改成功 保存平仓时的部分数据
        FutureCloseOrder futureCloseOrder=new FutureCloseOrder();
        futureCloseOrder.setCloseId(UUIDUtils.getUuid());
        futureCloseOrder.setOpenOrderRef(optionTradeDto.getOpenOrderRef());
        futureCloseOrder.setCustomerId(futureReportOrder.getCustomerId());
        futureCloseOrder.setInvestorId(futureOpenOrder.getInvestorId());
        futureCloseOrder.setProductCode(optionSymbol.getTargetCode());
        futureCloseOrder.setProductName(optionTradingGoods.getTradingGoodsName());
        futureCloseOrder.setInstrumentCode(optionTradeDto.getContraceCode());
        futureCloseOrder.setInstrumentName(optionSymbol.getInstrumentName());
        futureCloseOrder.setReportId(futureReportOrder.getReportId());
        /**暂时不给开仓价**/
//        futureCloseOrder.setOpenPrice(futureOpenOrder.getPrice());
        futureCloseOrder.setStatus(0);
        futureCloseOrder.setCreateTime(new Date());
        futureCloseOrder.setUpdateTime(new Date());
        futureCloseOrder.setOffsetFlag(futureReportOrder.getOffsetFlag());
        futureCloseOrderMapper.insertSelective(futureCloseOrder);

        //拼接tcp/ip传输数据串 reqorder|客户交易账号|母账户|合约code|本地报单编号|价格|手数|多空方向|开平方向|开仓本地报单编号|订单报单类型（0-用户普通报单，1-对冲单，2-跟单）|报单价格类型（0-市价 1-限价）
        //reqorder|dev10001001|5321101611|10001670|dev190304100015|0.0051|30|0|0|0|0|1
        String dataStr="reqorder|"+appCustomer.getCustomerTradeAccount()+"|" +futureOpenOrder.getInvestorId() +"|"+futureReportOrder.getInstrumentCode()+
                "|"+futureReportOrder.getOrderRef()+"|"+futureReportOrder.getLimitPrice()+"|"+futureReportOrder.getVolume()+
                "|"+futureReportOrder.getDirection()+"|"+hedgingOffsetFlag+"|"+optionTradeDto.getOpenOrderRef()+"|"+Constant.HEDGING_ORDER_FLAG+"|"+optionTradeDto.getOrderPriceType();
        SocketHandler socketHandler= null;
        try {
            /**记录次条报单是由那台机器发出的 60s后自动删除**/
            redisTemplate.opsForValue().set(Constant.ORDER_REF+futureReportOrder.getOrderRef(), LocalInetAddress.getLocalAddress(),12, TimeUnit.HOURS);
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            /**判断是否断开连接，断开返回true,没有返回false**/
            boolean flag=socketHandler.isServerClose();
            if(flag){
                throw new Exception("socket连接断开异常");
            }
            LOGGER.info("reqsHedgingReportOrder--->info:" + dataStr);
            socketHandler.sendMessage(dataStr);
            if(StringUtils.isNotBlank(optionTradeDto.getIdempotency())){
                redisTemplate.opsForValue().set(Constant.IDEMPOTENCY_REQSCLOSEREPORTORDER + optionTradeDto.getCustomerId() + ":" + optionTradeDto.getContraceCode(), optionTradeDto.getIdempotency(), 10, TimeUnit.MINUTES);
            }
//            mockTradeTask.mockReportOrder(this,dataStr);
            closeIsDocumentary(futureReportOrder,optionTradeDto.getOpenOrderRef());
            return ResponseEntity.ok(Message.REQ_SUCCESS);
        } catch (Exception e) {
            LOGGER.error("reqsCloseReportOrder connection socket err ....." , e);
            //手动回滚数据
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            if(StringUtils.isNotBlank(optionTradeDto.getIdempotency())) {
                redisTemplate.delete(Constant.IDEMPOTENCY_REQSOPENREPORTORDER + optionTradeDto.getCustomerId() + ":" + optionTradeDto.getContraceCode());
            }
            return ResponseEntity.ok(Message.SYSTEM_CONNECT_ERROR);
        }
    }
    @Transactional
    @Override
    public ResponseEntity<Message> respHedgingReportOrder(String respData) throws Exception {
        //交易日|报单提交状态|报单状态|报单编号|报单来源|报单类型|数量|最小成交量|今成交数量|报单价格|本地报单编号|合约|状态信息|报单时间|错误ID
        //交易日|报单提交状态|报单状态|报单编号|报单来源|报单类型|数量|最小成交量|今成交数量|报单价格|本地报单编号|合约|状态信息|报单时间|errid| (接口类型)
        //report|20190218|0|a|2008246|0|0|1|1|0|0.0035|a190218100016|10001703|报单已提交|14:51:52|0|0|0|
        //report|20190219|0|a||0|0|1|1|0|0.0024|a190219100004|10001668|报单已提交|10:16:11|0|4|0|0|
        LOGGER.info("respReportOrder:" + respData);
        String [] respReportData=respData.split("\\|");
        String tradingDay=respReportData[1],submitStatus=respReportData[2],orderStatus=respReportData[3],orderSysId=respReportData[4],orderSource=respReportData[5],
                orderType=respReportData[6],volume=respReportData[7],minVolume=respReportData[8],tradeVolume=respReportData[9],limitPrice=respReportData[10],
                orderRef=respReportData[11],instrumentCode=respReportData[12],statusMsg=respReportData[13],tradeTime=respReportData[14],errorCode=respReportData[15],
                tradeServerCode=respReportData[16];
        //匹配本地报单编号
        FutureReportOrder futureReportOrder=futureReportOrderMapper.selectByOrderRef(orderRef, instrumentCode);
        HedgingReportOrder hedgingReportOrder=hedgingReportOrderMapper.selectByOrderRef(orderRef,instrumentCode);
        if(futureReportOrder==null || hedgingReportOrder==null){
            LOGGER.error("respHedgingReportOrder futureReportOrder:"+futureReportOrder+"|hedgingReportOrder:"+hedgingReportOrder);
            LOGGER.error("respHedgingReportOrder c++ 传过来的本地报单编号在库中未找到：" + respData);
            return ResponseEntity.ok(Message.REPORT_ORDER_NOT_EXIST);
        }
        if(StringUtils.isNotBlank(futureReportOrder.getOrderStatus()) && ("5".equals(orderStatus) || "0".equals(orderStatus)) &&
                (futureReportOrder.getOrderStatus().contains("0") || futureReportOrder.getOrderStatus().contains("5"))){
            //报单已存在结束标识
            return ResponseEntity.ok(Message.REPORT_ORDER_END);
        }
        //如果出现错误回报则
        if(!"0".equals(errorCode)){
            /**orderStatus设置为错单**/
            orderStatus="99";
            statusMsg=CTPErrorCode.codeConvertMsg(errorCode);
            /**判断是否是份额不足导致失败**/
            if("9999".equals(errorCode) || "8888".equals(errorCode) || "31".equals(errorCode)){
                /**自动切换交易报单母账户逻辑**/
                switchOptionAccount(errorCode,1);
            }
        }
        futureReportOrder.setUpdateTime(new Date());
        futureReportOrder.setTradingDay(tradingDay);
        if(StringUtils.isNotBlank(futureReportOrder.getOrderStatus())){
            String dbOrderStatus=futureReportOrder.getOrderStatus();
            //获取最后一位状态
            String lastStatus=dbOrderStatus.substring(dbOrderStatus.length()-1);
            /**如果出现位置回报顺序错乱的情况**/
            if("0".equals(lastStatus) || "5".equals(lastStatus)){
                /**订单状态重新拼接**/
                String subOrderStatus=dbOrderStatus.substring(0,dbOrderStatus.length()-1);
                String newOrderStatus=subOrderStatus+orderStatus+"|"+lastStatus;
                futureReportOrder.setOrderStatus(newOrderStatus);
            }else{
                futureReportOrder.setOrderStatus(dbOrderStatus+orderStatus+"|");
            }
        }else{
            futureReportOrder.setOrderStatus(orderStatus + "|");
        }
        if(StringUtils.isNotBlank(futureReportOrder.getSubmitStatus())){
            String dbOrderStatus=futureReportOrder.getOrderStatus();
            String dbSubmitStatus=futureReportOrder.getSubmitStatus();
            //获取最后一位状态
            String lastOrderStatus=dbOrderStatus.substring(dbOrderStatus.length()-1);
            String lastSubmitStatus=dbSubmitStatus.substring(dbSubmitStatus.length()-1);
            /**如果出现位置回报顺序错乱的情况**/
            if("0".equals(lastOrderStatus) || "5".equals(lastOrderStatus)){
                /**订单状态重新拼接**/
                String subSubmitStatus=dbSubmitStatus.substring(0,dbSubmitStatus.length()-1);
                String newSubmitStatus=subSubmitStatus+orderStatus+"|"+lastSubmitStatus;
                futureReportOrder.setSubmitStatus(newSubmitStatus);
            }else{
                futureReportOrder.setSubmitStatus(dbSubmitStatus + submitStatus + "|");
            }
        }else{
            futureReportOrder.setSubmitStatus(submitStatus+"|");
        }
        futureReportOrder.setOrderSysId(orderSysId);
        futureReportOrder.setOrderSource(orderSource);
        futureReportOrder.setOrderType(orderType);
        futureReportOrder.setMinVolume(Integer.valueOf(minVolume));
        if(futureReportOrder.getTradeVolume()<Integer.valueOf(tradeVolume)){
            futureReportOrder.setTradeVolume(Integer.valueOf(tradeVolume));
        }
        futureReportOrder.setLimitPrice(new BigDecimal(limitPrice));
        futureReportOrder.setVolume(Integer.valueOf(volume));
        futureReportOrder.setStatusMsg(statusMsg);
        futureReportOrder.setReportTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
        futureReportOrder.setRespCStatus(1); //得到c++的响应标识
        futureReportOrder.setTradeServerCode(tradeServerCode);
        futureReportOrderMapper.updateByPrimaryKeySelective(futureReportOrder);
        /**修改对冲单报单数据及状态**/
        hedgingReportOrder.setTradingDay(tradingDay);
        hedgingReportOrder.setOrderStatus(futureReportOrder.getOrderStatus());
        hedgingReportOrder.setSubmitStatus(futureReportOrder.getSubmitStatus());
        hedgingReportOrder.setOrderSysId(orderSysId);
        hedgingReportOrder.setOrderSource(orderSource);
//        hedgingReportOrder.setOrderType(orderType);
        hedgingReportOrder.setMinVolume(Integer.valueOf(minVolume));
        hedgingReportOrder.setTradeVolume(futureReportOrder.getTradeVolume());
        hedgingReportOrder.setLimitPrice(new BigDecimal(limitPrice));
        hedgingReportOrder.setVolume(Integer.valueOf(volume));
        hedgingReportOrder.setStatusMsg(statusMsg);
        hedgingReportOrder.setReportTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
        hedgingReportOrder.setRespCStatus(1); //得到c++的响应标识
        hedgingReportOrder.setTradeServerCode(tradeServerCode);
        hedgingReportOrderMapper.updateByPrimaryKeySelective(hedgingReportOrder);
        /**封装报单推送模型**/
        PushSocketModel pushSocketModel=new PushSocketModel();
        pushSocketModel.setInstrumentName(futureReportOrder.getInstrumentName());
        pushSocketModel.setInstrumentCode(futureReportOrder.getInstrumentCode());
        pushSocketModel.setLimitPrice(futureReportOrder.getLimitPrice());
        pushSocketModel.setTradeVolume(futureReportOrder.getTradeVolume());
        pushSocketModel.setStatusMsg(futureReportOrder.getStatusMsg());
        pushSocketModel.setOrderNo(futureReportOrder.getOrderRef());
        pushSocketModel.setVolume(futureReportOrder.getVolume());
        pushSocketModel.setOrderPriceType(futureReportOrder.getOrderPriceType());
        //如果是开仓
        if("0".equals(hedgingReportOrder.getOffsetFlag())){
            //如果是平仓 且订单状态为撤单,错单
            if("5".equals(orderStatus) || "99".equals(orderStatus) || ("a".equals(orderStatus) && Constant.QWIN_OPTION_SERVER.equals(tradeServerCode))){
                FutureCloseOrder futureCloseOrder=futureCloseOrderMapper.selectByReportId(futureReportOrder.getReportId());
                //获取撤单手数
                int withdrawNum=futureReportOrder.getVolume()-futureReportOrder.getTradeVolume();
                //递归将扣除的可用手数加回来
                this.replyEffectiveHoldVolume(futureCloseOrder.getOpenOrderRef(), withdrawNum);
                /**撤单-异步推送socket**/
                sendNotifyChannel(futureReportOrder.getCustomerId(), Constant.TRADE_ORDER, pushSocketModel);
            }
        }
        /**增加过程状态推送 3-报单已提交 1-部分成交**/
        if("3".equals(orderStatus) || "1".equals(orderStatus)){
            /**报单过程状态推送**/
            sendNotifyChannel(futureReportOrder.getCustomerId(), Constant.REPORT_PROCESS_ORDER, pushSocketModel);
        }
        return ResponseEntity.ok(Message.REQ_SUCCESS);
    }

    /**
     * 对冲单成交回报
     * @param respData
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public ResponseEntity<Message> respHedgingDealOrder(String respData) throws Exception {
        //开平方向|本地报单编号|合约code|报单编号|成交编号|成交日期|成交时间|手数|价格|买卖类型|交易类型
        //hedging|4|180927100195|RM901|      204205|       80029|20180927|14:55:01|1|2484.000|1|0|
        LOGGER.info("respHedgingOrder:" + respData);
        String [] respDealData=respData.split("\\|");
        String offsetFlag=respDealData[1],orderRef=respDealData[2],instrumentCode=respDealData[3],orderSysId=respDealData[4],
                tradeId=respDealData[5],tradingDay=respDealData[6],tradeTime=respDealData[7],volume=respDealData[8],price=respDealData[9],
                direction=respDealData[10],tradeType=respDealData[11];
        //获取开平标识
        //匹配本地报单编号 orderRef,instrumentCode
        FutureReportOrder futureReportOrder=futureReportOrderMapper.selectByOrderRef(orderRef, instrumentCode);
        if(futureReportOrder==null){
            LOGGER.error("对冲单成交--传过来的本地报单编号在库中未找到：" + respData);
            return ResponseEntity.ok(Message.REPORT_ORDER_NOT_EXIST);
        }
        String nowDate=DateUtils.convertDateToString(new Date());
        Object optionSymbolObj=getRedisSymbol(nowDate, instrumentCode, 7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, instrumentCode);
        OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
        /**根据商品code查询商品的 最小波动点位等信息**/
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionSymbol.getTargetCode());
        BigDecimal actualVolume=new BigDecimal(volume);
        PushSocketModel pushSocketModel=new PushSocketModel();
        String businessInfoId=agencyBusinessMapper.selectByBusinessLevel().getUniqueIdentification();
        /**获取开仓时的费率 开仓手续费/张 平仓手续费/张**/
        BigDecimal univalentOpenFee=futureReportOrder.getUnivalentOpenFee();
        BigDecimal univalentCloseFee=futureReportOrder.getUnivalentCloseFee();
//        BigDecimal univalentCostOpenFee=futureReportOrder.getUnivalentCostOpenFee();
        BigDecimal univalentCostCloseFee=futureReportOrder.getUnivalentCostCloseFee();
        /**因为是对冲单 所以对冲单响应的开仓实际对应的是用户平仓**/
        if(Integer.valueOf(offsetFlag)==0){
            /**记录对冲开仓成交单**/
            HedgingOpenOrder hedgingOpenOrder=new HedgingOpenOrder();
            hedgingOpenOrder.setOpenId(UUIDUtils.getUuid());
            hedgingOpenOrder.setCustomerId(futureReportOrder.getCustomerId());
            hedgingOpenOrder.setInvestorId(futureReportOrder.getInvestorId());
            hedgingOpenOrder.setProductCode(futureReportOrder.getProductCode());
            hedgingOpenOrder.setProductName(futureReportOrder.getProductName());
            hedgingOpenOrder.setInstrumentCode(futureReportOrder.getInstrumentCode());
            hedgingOpenOrder.setInstrumentName(futureReportOrder.getInstrumentName());
            hedgingOpenOrder.setReportId(futureReportOrder.getReportId());
            hedgingOpenOrder.setOrderSysId(orderSysId);
            hedgingOpenOrder.setTradeId(tradeId);
            hedgingOpenOrder.setTradingDay(tradingDay);
            hedgingOpenOrder.setTradeTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
            hedgingOpenOrder.setVolume(Integer.valueOf(volume));
            hedgingOpenOrder.setPrice(new BigDecimal(price).setScale(4, BigDecimal.ROUND_HALF_UP));
            hedgingOpenOrder.setOffsetFlag(offsetFlag);
            hedgingOpenOrder.setDirection(direction);
            hedgingOpenOrder.setTradeType(tradeType);
            hedgingOpenOrder.setOrderRef(orderRef);
            hedgingOpenOrder.setStatus(1);
            hedgingOpenOrder.setHoldVolume(Integer.valueOf(volume));
            hedgingOpenOrder.setStopLossPoint(futureReportOrder.getStopLossPoint());
            hedgingOpenOrder.setEffectiveHoldVolume(Integer.valueOf(volume));//可用手数等于成功报单手数
            /**实际单笔开仓费用 开仓价*合约乘数**/
            BigDecimal actualSinglePrice=new BigDecimal(price).multiply(optionTradingGoods.getTradingContractMultiplier());
            /**开仓使用金额**/
            hedgingOpenOrder.setMargin(actualSinglePrice.multiply(actualVolume));
            hedgingOpenOrder.setOpenFee(univalentOpenFee.multiply(actualVolume));
            hedgingOpenOrderMapper.insertSelective(hedgingOpenOrder);
            /**平仓回报**/
            FutureCloseOrder futureCloseOrder=futureCloseOrderMapper.selectByReportId(futureReportOrder.getReportId());
            futureCloseOrder.setCloseId(UUIDUtils.getUuid());
            futureCloseOrder.setCustomerId(futureReportOrder.getCustomerId());
            futureCloseOrder.setInvestorId(futureReportOrder.getInvestorId());
            futureCloseOrder.setProductCode(futureReportOrder.getProductCode());
            futureCloseOrder.setProductName(futureReportOrder.getProductName());
            futureCloseOrder.setInstrumentCode(futureReportOrder.getInstrumentCode());
            futureCloseOrder.setInstrumentName(futureReportOrder.getInstrumentName());
            futureCloseOrder.setOrderSysId(orderSysId);
            futureCloseOrder.setTradeId(tradeId);
            futureCloseOrder.setTradingDay(tradingDay);
            futureCloseOrder.setTradeTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
            futureCloseOrder.setVolume(Integer.valueOf(volume));
            futureCloseOrder.setClosePrice(new BigDecimal(price));
            /**计算平仓手续费**/
            BigDecimal closeFee=univalentCloseFee.multiply(new BigDecimal(volume));
            /**计算开仓手续费**/
            BigDecimal openFee=univalentOpenFee.multiply(new BigDecimal(volume));
            futureCloseOrder.setCloseFee(closeFee);
            futureCloseOrder.setOpenFee(openFee);
            /**查询平仓对应的开仓数据并修改开仓表中的当前持仓手数**/
            /**获取该笔平仓的开仓报单本地引用**/
            List<FutureOpenOrder> futureOpenOrderList=futureOpenOrderMapper.selectListByOrderRef(futureCloseOrder.getOpenOrderRef());
            Double sumMarketValue=0.00;
            Integer sumVolume=0;
            for(FutureOpenOrder obj:futureOpenOrderList){
                //计算市值
                BigDecimal marketValue=obj.getPrice().multiply(new BigDecimal(obj.getVolume()));
                sumMarketValue+=marketValue.doubleValue();
                sumVolume+=obj.getVolume();
            }
            /**计算持仓均价**/
            BigDecimal avgPrice=new BigDecimal(sumMarketValue/sumVolume).setScale(4, BigDecimal.ROUND_HALF_UP);
            /**计算平仓盈亏**/
            BigDecimal closeProfitLoss=null;
            /**平仓权利金=合约乘数*平仓价格*成交手数**/
            BigDecimal closePremium=optionTradingGoods.getTradingContractMultiplier().multiply(new BigDecimal(price)).multiply(actualVolume);
            /**开仓权利金=合约乘数*持仓均价*成交手数**/
            BigDecimal openPremium=optionTradingGoods.getTradingContractMultiplier().multiply(avgPrice).multiply(actualVolume);
            /**平仓盈亏=平仓权利金-开仓权利金**/
            closeProfitLoss=closePremium.subtract(openPremium);
            /**开仓价为持仓均价**/
            futureCloseOrder.setOpenPrice(avgPrice);
            futureCloseOrder.setCloseProfitLoss(closeProfitLoss);
            futureCloseOrder.setDirection(direction);
            futureCloseOrder.setTradeType(tradeType);
            futureCloseOrder.setOrderRef(orderRef);
//            futureCloseOrder.setOffsetFlag(offsetFlag);  因为是对冲单 在记录部分数据的时候已经把报单类型给记录上了
            futureCloseOrder.setCreateTime(new Date());
            futureCloseOrder.setUpdateTime(new Date());
            futureCloseOrder.setStatus(1);
            futureCloseOrder.setClosePremium(closePremium);
            futureCloseOrder.setOpenPremium(openPremium);
            //保存来自c++的平仓对象
            futureCloseOrderMapper.insertSelective(futureCloseOrder);
            this.buckleHoldVolume(futureCloseOrder.getOpenOrderRef(),futureCloseOrder.getVolume());
            /****************更新用户资金操作******************/
            AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(futureCloseOrder.getCustomerId());
            //处理当前资金 资金=当前资金+(平仓权利金-(平仓手续费*实际成交手数))
            Double closeActualCapital=closePremium.subtract(univalentCloseFee.multiply(actualVolume)).doubleValue();
            appCustomer.setCustomerCapital(closeActualCapital);
            //处理保证金 冻结资金=当前冻结资金-(平仓手续费*实际成交手数)
            Double closeFreezeCapital=(univalentCloseFee.multiply(actualVolume)).doubleValue();
            appCustomer.setCustomerFreezeCapital(-closeFreezeCapital);
//            LOGGER.info("respDealOrderClose----->refreshCapital:" + appCustomer.toString());
            LOGGER.info(appCustomer.getCustomerTradeAccount()+"refreshCapital respDealOrderClose:操作冻结" + appCustomer.getCustomerFreezeCapital()+"|操作资金"+appCustomer.getCustomerCapital());
            appCustomerMapper.refreshCapital(appCustomer);
            /**封装成交推送数据包**/
            pushSocketModel.setInstrumentName(futureCloseOrder.getInstrumentName());
            pushSocketModel.setInstrumentCode(futureCloseOrder.getInstrumentCode());
            pushSocketModel.setLimitPrice(futureCloseOrder.getClosePrice());
            pushSocketModel.setTradeVolume(futureCloseOrder.getVolume());
            pushSocketModel.setOrderNo(futureCloseOrder.getOrderRef());
            pushSocketModel.setStatusMsg("成功");
            /**添加机构冻结资金Begin!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**/
            //处理当前资金 资金=当前资金+(平仓权利金-(平仓成本手续费*实际成交手数))
            Double closeCostActualCapital=closePremium.subtract(univalentCostCloseFee.multiply(actualVolume)).doubleValue();
            //处理保证金 冻结资金=当前冻结资金-(平仓成本手续费*实际成交手数)
            Double closeCostFreezeCapital=(univalentCostCloseFee.multiply(actualVolume)).doubleValue();
            tradeRemoteCall.tradeCapitalChange(businessInfoId,orderRef,"4","0",volume,closeCostActualCapital,closeCostFreezeCapital);
            /**添加机构冻结资金End**/
            /**推送资金记录平仓**/
            tradeRemoteCall.sendCashFlowToCrm(futureCloseOrder.getCloseId(), 3,closeActualCapital,closeFreezeCapital);
            sendNotifyChannel(futureReportOrder.getCustomerId(), Constant.DEAL_ORDER, pushSocketModel);
        }
        return ResponseEntity.ok(Message.REQ_SUCCESS);
    }

}
